Skip to content

Commit

Permalink
Merge pull request #19 from ava-labs/keychain
Browse files Browse the repository at this point in the history
Key, Ledger, Keychain
  • Loading branch information
felipemadero authored May 28, 2024
2 parents dfc3414 + 4749cb0 commit aedef85
Show file tree
Hide file tree
Showing 14 changed files with 373 additions and 40 deletions.
18 changes: 0 additions & 18 deletions avalanche/keychain.go

This file was deleted.

21 changes: 17 additions & 4 deletions avalanche/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@

package avalanche

import "github.com/ava-labs/avalanchego/utils/constants"

type NetworkKind int64

type Network struct {
Kind NetworkKind

ID uint32

Kind NetworkKind
ID uint32
Endpoint string
}

func (n Network) HRP() string {
switch n.ID {
case constants.LocalID:
return constants.LocalHRP
case constants.FujiID:
return constants.FujiHRP
case constants.MainnetID:
return constants.MainnetHRP
default:
return constants.FallbackHRP
}
}
12 changes: 10 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ go 1.21.9

toolchain go1.21.10

require github.com/ava-labs/avalanchego v1.11.5
require (
github.com/ava-labs/avalanchego v1.11.5
golang.org/x/exp v0.0.0-20231127185646-65229373498e
)

require (
github.com/DataDog/zstd v1.5.2 // indirect
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e // indirect
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/VictoriaMetrics/fastcache v1.10.0 // indirect
github.com/ava-labs/coreth v0.13.3-rc.2 // indirect
github.com/ava-labs/ledger-avalanche/go v0.0.0-20231102202641-ae2ebdaeac34 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.7.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
Expand Down Expand Up @@ -88,10 +94,13 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tyler-smith/go-bip32 v1.0.0 // indirect
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
github.com/urfave/cli/v2 v2.25.7 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
github.com/zondax/hid v0.9.2 // indirect
github.com/zondax/ledger-go v0.14.3 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect
Expand All @@ -104,7 +113,6 @@ require (
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
Expand Down
18 changes: 18 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e h1:ahyvB3q25YnZWly5Gq1ekg6jcmWaGj/vG/MhF4aisoc=
github.com/FactomProject/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:kGUqhHd//musdITWjFvNTHn90WG9bMLBEPQZ17Cmlpw=
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec h1:1Qb69mGp/UtRPn422BH4/Y4Q3SLUrD9KHuDkm8iodFc=
github.com/FactomProject/btcutilecc v0.0.0-20130527213604-d3a63a5752ec/go.mod h1:CD8UlnlLDiqb36L110uqiP2iSflVjx9g/3U9hCI4q2U=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
Expand All @@ -22,6 +26,8 @@ github.com/ava-labs/avalanchego v1.11.5 h1:3HCE6I1/9BOSrIvwXXuOhVuMGMi926jKhQ+sg
github.com/ava-labs/avalanchego v1.11.5/go.mod h1:XPNUEh0ezPEW1IV6yo6AwvqjTZSG1twGskDb7dVcIRA=
github.com/ava-labs/coreth v0.13.3-rc.2 h1:lhyQwln6at1DTs1O586dMSAtGtSfQWlt2WH+Z2kgYdQ=
github.com/ava-labs/coreth v0.13.3-rc.2/go.mod h1:4l15XGak3FklhIb7CtlC/1YVwGAfMl83R2zd2N0hNE0=
github.com/ava-labs/ledger-avalanche/go v0.0.0-20231102202641-ae2ebdaeac34 h1:mg9Uw6oZFJKytJxgxnl3uxZOs/SB8CVHg6Io4Tf99Zc=
github.com/ava-labs/ledger-avalanche/go v0.0.0-20231102202641-ae2ebdaeac34/go.mod h1:pJxaT9bUgeRNVmNRgtCHb7sFDIRKy7CzTQVi8gGNT6g=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
Expand Down Expand Up @@ -66,6 +72,8 @@ github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86c
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e h1:0XBUw73chJ1VYSsfvcPvVT7auykAJce9FpRr10L6Qhw=
github.com/cmars/basen v0.0.0-20150613233007-fe3947df716e/go.mod h1:P13beTBKr5Q18lJe1rIoLUqjM+CB1zYrRg44ZqGuQSA=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
github.com/cockroachdb/datadriven v1.0.3-0.20230801171734-e384cf455877 h1:1MLK4YpFtIEo3ZtMA5C795Wtv5VuUnrXX7mQG+aHg6o=
Expand Down Expand Up @@ -400,6 +408,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.1.5-0.20170601210322-f6abca593680/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand All @@ -419,6 +428,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tyler-smith/go-bip32 v1.0.0 h1:sDR9juArbUgX+bO/iblgZnMPeWY1KZMUC2AFUJdv5KE=
github.com/tyler-smith/go-bip32 v1.0.0/go.mod h1:onot+eHknzV4BVPwrzqY5OoVpyCvnwD7lMawL5aQupE=
github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
Expand Down Expand Up @@ -449,6 +460,10 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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=
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
Expand All @@ -473,6 +488,7 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down Expand Up @@ -676,5 +692,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54=
launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
125 changes: 125 additions & 0 deletions keychain/keychain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package keychain

import (
"fmt"

"avalanche-tooling-sdk-go/avalanche"
"avalanche-tooling-sdk-go/key"
"avalanche-tooling-sdk-go/ledger"
"avalanche-tooling-sdk-go/utils"

"github.com/ava-labs/avalanchego/utils/crypto/keychain"

"golang.org/x/exp/maps"
)

type Keychain struct {
keychain.Keychain
network avalanche.Network
ledgerDevice *ledger.LedgerDevice
ledgerIndices []uint32
}

func (kc *Keychain) P() ([]string, error) {
return utils.P(kc.network.HRP(), kc.Addresses().List())
}

func (kc *Keychain) LedgerEnabled() bool {
return kc.ledgerDevice != nil
}

func (kc *Keychain) AddLedgerIndices(indices []uint32) error {
if kc.LedgerEnabled() {
kc.ledgerIndices = utils.Unique(append(kc.ledgerIndices, indices...))
utils.Uint32Sort(kc.ledgerIndices)
newKc, err := keychain.NewLedgerKeychainFromIndices(kc.ledgerDevice, kc.ledgerIndices)
if err != nil {
return err
}
kc.Keychain = newKc
return nil
}
return fmt.Errorf("keychain is not ledger enabled")
}

func (kc *Keychain) AddLedgerAddresses(addresses []string) error {
if kc.LedgerEnabled() {
indices, err := kc.ledgerDevice.FindAddresses(addresses, 0)
if err != nil {
return err
}
return kc.AddLedgerIndices(maps.Values(indices))
}
return fmt.Errorf("keychain is not ledger enabled")
}

func (kc *Keychain) AddLedgerFunds(amount uint64) error {
if kc.LedgerEnabled() {
indices, err := kc.ledgerDevice.FindFunds(kc.network, amount, 0)
if err != nil {
return err
}
return kc.AddLedgerIndices(indices)
}
return fmt.Errorf("keychain is not ledger enabled")
}

func NewKeychain(
network avalanche.Network,
keyPath string,
useEwoq bool,
useLedger bool,
ledgerAddresses []string,
requiredFunds uint64,
) (*Keychain, error) {
// get keychain accessor
if useLedger {
dev, err := ledger.New()
if err != nil {
return nil, err
}
kc := Keychain{
ledgerDevice: dev,
network: network,
}
if err := kc.AddLedgerIndices([]uint32{0}); err != nil {
return nil, err
}
if requiredFunds > 0 {
if err := kc.AddLedgerFunds(requiredFunds); err != nil {
return nil, err
}
}
if len(ledgerAddresses) > 0 {
if err := kc.AddLedgerAddresses(ledgerAddresses); err != nil {
return nil, err
}
}
return &kc, nil
}
if useEwoq {
sf, err := key.LoadEwoq()
if err != nil {
return nil, err
}
kc := Keychain{
Keychain: sf.KeyChain(),
network: network,
}
return &kc, nil
}
if keyPath != "" {
sf, err := key.LoadSoft(keyPath)
if err != nil {
return nil, err
}
kc := Keychain{
Keychain: sf.KeyChain(),
network: network,
}
return &kc, nil
}
return nil, fmt.Errorf("not keychain option defined")
}
108 changes: 108 additions & 0 deletions ledger/ledger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (C) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ledger

import (
"avalanche-tooling-sdk-go/avalanche"
"avalanche-tooling-sdk-go/utils"
"fmt"

"github.com/ava-labs/avalanchego/utils/crypto/keychain"
"github.com/ava-labs/avalanchego/utils/crypto/ledger"
"github.com/ava-labs/avalanchego/utils/formatting/address"
"github.com/ava-labs/avalanchego/vms/platformvm"
)

const (
maxIndexToSearch = 1000
maxIndexToSearchForBalance = 100
)

type LedgerDevice struct {
keychain.Ledger
}

func New() (*LedgerDevice, error) {
avagoDev, err := ledger.New()
if err != nil {
return nil, err
}
dev := LedgerDevice{
Ledger: avagoDev,
}
return &dev, nil
}

func (dev *LedgerDevice) P(network avalanche.Network, indices []uint32) ([]string, error) {
addresses, err := dev.Addresses(indices)
if err != nil {
return nil, err
}
return utils.P(network.HRP(), addresses)
}

func (dev *LedgerDevice) FindAddresses(addresses []string, maxIndex uint32) (map[string]uint32, error) {
addressesIDs, err := address.ParseToIDs(addresses)
if err != nil {
return nil, fmt.Errorf("failure parsing ledger addresses: %w", err)
}
// for all ledger indices to search for, find if the ledger address belongs to the input
// addresses and, if so, add an index association to indexMap.
// breaks the loop if all addresses were found
if maxIndex == 0 {
maxIndex = maxIndexToSearch
}
indices := map[string]uint32{}
for index := uint32(0); index < maxIndex; index++ {
ledgerAddress, err := dev.Addresses([]uint32{index})
if err != nil {
return nil, err
}
for addressIndex, addr := range addressesIDs {
if addr == ledgerAddress[0] {
indices[addresses[addressIndex]] = index
}
}
if len(indices) == len(addresses) {
break
}
}
return indices, nil
}

// search for a set of indices that pay a given amount
func (dev *LedgerDevice) FindFunds(
network avalanche.Network,
amount uint64,
maxIndex uint32,
) ([]uint32, error) {
pClient := platformvm.NewClient(network.Endpoint)
totalBalance := uint64(0)
indices := []uint32{}
if maxIndex == 0 {
maxIndex = maxIndexToSearchForBalance
}
for index := uint32(0); index < maxIndex; index++ {
ledgerAddress, err := dev.Addresses([]uint32{index})
if err != nil {
return []uint32{}, err
}
ctx, cancel := utils.GetAPIContext()
resp, err := pClient.GetBalance(ctx, ledgerAddress)
cancel()
if err != nil {
return nil, err
}
if resp.Balance > 0 {
totalBalance += uint64(resp.Balance)
indices = append(indices, index)
}
if totalBalance >= amount {
break
}
}
if totalBalance < amount {
return nil, fmt.Errorf("not enough funds on ledger")
}
return indices, nil
}
Loading

0 comments on commit aedef85

Please sign in to comment.