Skip to content

Commit

Permalink
Add ledger tx parsing support (ava-labs#2695)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipemadero authored Mar 4, 2023
1 parent 4c4bf62 commit c1dfcbc
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 8 deletions.
22 changes: 22 additions & 0 deletions utils/crypto/keychain/keychain.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var (
// to sign a hash
type Signer interface {
SignHash([]byte) ([]byte, error)
Sign([]byte) ([]byte, error)
Address() ids.ShortID
}

Expand Down Expand Up @@ -120,6 +121,7 @@ func (l *ledgerKeychain) Get(addr ids.ShortID) (Signer, bool) {
}, true
}

// expects to receive a hash of the unsigned tx bytes
func (l *ledgerSigner) SignHash(b []byte) ([]byte, error) {
// Sign using the address with index l.idx on the ledger device. The number
// of returned signatures should be the same length as the provided indices.
Expand All @@ -139,6 +141,26 @@ func (l *ledgerSigner) SignHash(b []byte) ([]byte, error) {
return sigs[0], err
}

// expects to receive the unsigned tx bytes
func (l *ledgerSigner) Sign(b []byte) ([]byte, error) {
// Sign using the address with index l.idx on the ledger device. The number
// of returned signatures should be the same length as the provided indices.
sigs, err := l.ledger.Sign(b, []uint32{l.idx})
if err != nil {
return nil, err
}

if sigsLen := len(sigs); sigsLen != 1 {
return nil, fmt.Errorf(
"%w. expected 1, got %d",
ErrInvalidNumSignatures,
sigsLen,
)
}

return sigs[0], err
}

func (l *ledgerSigner) Address() ids.ShortID {
return l.addr
}
2 changes: 1 addition & 1 deletion utils/crypto/keychain/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ type Ledger interface {
Address(displayHRP string, addressIndex uint32) (ids.ShortID, error)
Addresses(addressIndices []uint32) ([]ids.ShortID, error)
SignHash(hash []byte, addressIndices []uint32) ([][]byte, error)
// TODO: add SignTransaction
Sign(unsignedTxBytes []byte, addressIndices []uint32) ([][]byte, error)
Disconnect() error
}
15 changes: 15 additions & 0 deletions utils/crypto/keychain/mock_ledger.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 35 additions & 1 deletion utils/crypto/ledger/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import (

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/crypto/keychain"
"github.com/ava-labs/avalanchego/utils/hashing"
"github.com/ava-labs/avalanchego/version"
)

const rootPath = "m/44'/9000'/0'"
const (
rootPath = "m/44'/9000'/0'"
ledgerBufferLimit = 8192
ledgerPathSize = 9
)

var _ keychain.Ledger = (*Ledger)(nil)

Expand Down Expand Up @@ -79,6 +84,35 @@ func (l *Ledger) SignHash(hash []byte, addressIndices []uint32) ([][]byte, error
return responses, nil
}

func (l *Ledger) Sign(txBytes []byte, addressIndices []uint32) ([][]byte, error) {
// will pass to the ledger addressIndices both as signing paths and change paths
numSigningPaths := len(addressIndices)
numChangePaths := len(addressIndices)
if len(txBytes)+(numSigningPaths+numChangePaths)*ledgerPathSize > ledgerBufferLimit {
// There is a limit on the tx length that can be parsed by the ledger
// app. When the tx that is being signed is too large, we sign with hash
// instead.
//
// Ref: https://github.com/ava-labs/avalanche-wallet-sdk/blob/9a71f05e424e06b94eaccf21fd32d7983ed1b040/src/Wallet/Ledger/provider/ZondaxProvider.ts#L68
unsignedHash := hashing.ComputeHash256(txBytes)
return l.SignHash(unsignedHash, addressIndices)
}
strIndices := convertToSigningPaths(addressIndices)
response, err := l.device.Sign(rootPath, strIndices, txBytes, strIndices)
if err != nil {
return nil, fmt.Errorf("%w: unable to sign transaction", err)
}
responses := make([][]byte, len(strIndices))
for i, index := range strIndices {
sig, ok := response.Signature[index]
if !ok {
return nil, fmt.Errorf("missing signature %s", index)
}
responses[i] = sig
}
return responses, nil
}

func (l *Ledger) Version() (*version.Semantic, error) {
resp, err := l.device.GetVersion()
if err != nil {
Expand Down
4 changes: 1 addition & 3 deletions wallet/chain/p/signer_visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/crypto/keychain"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
"github.com/ava-labs/avalanchego/utils/hashing"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/components/verify"
"github.com/ava-labs/avalanchego/vms/platformvm/stakeable"
Expand Down Expand Up @@ -266,7 +265,6 @@ func sign(tx *txs.Tx, txSigners [][]keychain.Signer) error {
if err != nil {
return fmt.Errorf("couldn't marshal unsigned tx: %w", err)
}
unsignedHash := hashing.ComputeHash256(unsignedBytes)

if expectedLen := len(txSigners); expectedLen != len(tx.Creds) {
tx.Creds = make([]verify.Verifiable, expectedLen)
Expand Down Expand Up @@ -309,7 +307,7 @@ func sign(tx *txs.Tx, txSigners [][]keychain.Signer) error {
continue
}

sig, err := signer.SignHash(unsignedHash)
sig, err := signer.Sign(unsignedBytes)
if err != nil {
return fmt.Errorf("problem signing tx: %w", err)
}
Expand Down
4 changes: 1 addition & 3 deletions wallet/chain/x/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/utils/crypto/keychain"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
"github.com/ava-labs/avalanchego/utils/hashing"
"github.com/ava-labs/avalanchego/vms/avm/fxs"
"github.com/ava-labs/avalanchego/vms/avm/txs"
"github.com/ava-labs/avalanchego/vms/components/avax"
Expand Down Expand Up @@ -262,7 +261,6 @@ func sign(tx *txs.Tx, creds []verify.Verifiable, txSigners [][]keychain.Signer)
if err != nil {
return fmt.Errorf("couldn't marshal unsigned tx: %w", err)
}
unsignedHash := hashing.ComputeHash256(unsignedBytes)

if expectedLen := len(txSigners); expectedLen != len(tx.Creds) {
tx.Creds = make([]*fxs.FxCredential, expectedLen)
Expand Down Expand Up @@ -318,7 +316,7 @@ func sign(tx *txs.Tx, creds []verify.Verifiable, txSigners [][]keychain.Signer)
continue
}

sig, err := signer.SignHash(unsignedHash)
sig, err := signer.Sign(unsignedBytes)
if err != nil {
return fmt.Errorf("problem signing tx: %w", err)
}
Expand Down

0 comments on commit c1dfcbc

Please sign in to comment.