Skip to content

Commit

Permalink
feat(wallet): Add Ledger runtime support
Browse files Browse the repository at this point in the history
  • Loading branch information
matevz committed Mar 13, 2023
1 parent 0dec24c commit fa874db
Show file tree
Hide file tree
Showing 12 changed files with 413 additions and 118 deletions.
30 changes: 17 additions & 13 deletions cmd/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"math/big"
"os"

ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"

Expand All @@ -15,9 +16,9 @@ import (
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
roothash "github.com/oasisprotocol/oasis-core/go/roothash/api"
staking "github.com/oasisprotocol/oasis-core/go/staking/api"

"github.com/oasisprotocol/oasis-sdk/client-sdk/go/client"
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/connection"
sdkSignature "github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature"
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/helpers"
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/modules/accounts"
"github.com/oasisprotocol/oasis-sdk/client-sdk/go/modules/consensusaccounts"
Expand Down Expand Up @@ -66,7 +67,7 @@ var (
c, err := connection.Connect(ctx, npa.Network)
cobra.CheckErr(err)

addr, err := common.ResolveLocalAccountOrAddress(npa.Network, targetAddress)
addr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, targetAddress)
cobra.CheckErr(err)

height, err := common.GetActualHeight(
Expand Down Expand Up @@ -235,7 +236,7 @@ var (
}

// Resolve beneficiary address.
benAddr, err := common.ResolveLocalAccountOrAddress(npa.Network, beneficiary)
benAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, beneficiary)
cobra.CheckErr(err)

// Parse amount.
Expand Down Expand Up @@ -294,9 +295,10 @@ var (

// Resolve destination address when specified.
var toAddr *types.Address
var toEthAddr *ethCommon.Address
if to != "" {
var err error
toAddr, err = common.ResolveLocalAccountOrAddress(npa.Network, to)
toAddr, toEthAddr, err = common.ResolveLocalAccountOrAddress(npa.Network, to)
cobra.CheckErr(err)
}

Expand All @@ -318,7 +320,8 @@ var (
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx)
txDetails := sdkSignature.TxDetails{OrigTo: toEthAddr}
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, &txDetails)
cobra.CheckErr(err)

if txCfg.Offline {
Expand Down Expand Up @@ -395,12 +398,12 @@ var (
var addrToCheck string
if to != "" {
var err error
toAddr, err = common.ResolveLocalAccountOrAddress(npa.Network, to)
toAddr, _, err = common.ResolveLocalAccountOrAddress(npa.Network, to)
cobra.CheckErr(err)
addrToCheck = toAddr.String()
} else {
// Destination address is implicit, but obtain it for safety check below nonetheless.
addr, err := helpers.ResolveAddress(npa.Network, npa.Account.Address)
addr, _, err := helpers.ResolveAddress(npa.Network, npa.Account.Address)
cobra.CheckErr(err)
addrToCheck = addr.String()
}
Expand All @@ -423,7 +426,7 @@ var (
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil)
cobra.CheckErr(err)

if txCfg.Offline {
Expand Down Expand Up @@ -490,7 +493,7 @@ var (
}

// Resolve destination address.
toAddr, err := common.ResolveLocalAccountOrAddress(npa.Network, to)
toAddr, toEthAddr, err := common.ResolveLocalAccountOrAddress(npa.Network, to)
cobra.CheckErr(err)

// Check, if to address is known to be unspendable.
Expand Down Expand Up @@ -528,7 +531,8 @@ var (
Amount: *amountBaseUnits,
})

sigTx, meta, err = common.SignParaTimeTransaction(ctx, npa, acc, conn, tx)
txDetails := sdkSignature.TxDetails{OrigTo: toEthAddr}
sigTx, meta, err = common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, &txDetails)
cobra.CheckErr(err)
}

Expand Down Expand Up @@ -605,7 +609,7 @@ var (
}

// Resolve destination address.
toAddr, err := common.ResolveLocalAccountOrAddress(npa.Network, to)
toAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, to)
cobra.CheckErr(err)

acc := common.LoadAccount(cfg, npa.AccountName)
Expand Down Expand Up @@ -658,7 +662,7 @@ var (
}

// Resolve destination address.
fromAddr, err := common.ResolveLocalAccountOrAddress(npa.Network, from)
fromAddr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, from)
cobra.CheckErr(err)

acc := common.LoadAccount(cfg, npa.AccountName)
Expand Down Expand Up @@ -728,7 +732,7 @@ var (
now, err = conn.Consensus().Beacon().GetEpoch(ctx, height)
cobra.CheckErr(err)

addr, err := common.ResolveLocalAccountOrAddress(npa.Network, npa.Account.Address)
addr, _, err := common.ResolveLocalAccountOrAddress(npa.Network, npa.Account.Address)
cobra.CheckErr(err)

stakingConn := conn.Consensus().Staking()
Expand Down
9 changes: 7 additions & 2 deletions cmd/common/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ func SignParaTimeTransaction(
wallet wallet.Account,
conn connection.Connection,
tx *types.Transaction,
txDetails *signature.TxDetails,
) (*types.UnverifiedTransaction, interface{}, error) {
// Default to passed values and do online estimation when possible.
nonce := txNonce
Expand Down Expand Up @@ -240,12 +241,16 @@ func SignParaTimeTransaction(
PrintTransactionBeforeSigning(npa, tx)

// Sign the transaction.
sigCtx := signature.DeriveChainContext(npa.ParaTime.Namespace(), npa.Network.ChainContext)
ts := tx.PrepareForSigning()
sigCtx := &signature.RichContext{
RuntimeID: npa.ParaTime.Namespace(),
ChainContext: npa.Network.ChainContext,
Base: types.SignatureContextBase,
TxDetails: txDetails,
}
if err := ts.AppendSign(sigCtx, wallet.Signer()); err != nil {
return nil, nil, fmt.Errorf("failed to sign transaction: %w", err)
}

return ts.UnverifiedTransaction(), meta, nil
}

Expand Down
8 changes: 5 additions & 3 deletions cmd/common/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

"github.com/AlecAivazis/survey/v2"
ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/spf13/cobra"

staking "github.com/oasisprotocol/oasis-core/go/staking/api"
Expand Down Expand Up @@ -78,17 +79,18 @@ func LoadTestAccountConfig(name string) (*config.Account, error) {
}

// ResolveLocalAccountOrAddress resolves a string address into the corresponding account address.
func ResolveLocalAccountOrAddress(net *configSdk.Network, address string) (*types.Address, error) {
func ResolveLocalAccountOrAddress(net *configSdk.Network, address string) (*types.Address, *ethCommon.Address, error) {
// Check if address is the account name in the wallet.
if acc, ok := config.Global().Wallet.All[address]; ok {
addr := acc.GetAddress()
return &addr, nil
// TODO: Implement acc.GetEthAddress()
return &addr, nil, nil
}

// Check if address is the name of an address book entry.
if entry, ok := config.Global().AddressBook.All[address]; ok {
addr := entry.GetAddress()
return &addr, nil
return &addr, entry.GetEthAddress(), nil
}

return helpers.ResolveAddress(net, address)
Expand Down
10 changes: 5 additions & 5 deletions cmd/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ otherwise as Base64.`,
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil)
cobra.CheckErr(err)

var result contracts.UploadResult
Expand Down Expand Up @@ -332,7 +332,7 @@ otherwise as Base64.`,
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil)
cobra.CheckErr(err)

var result contracts.InstantiateResult
Expand Down Expand Up @@ -389,7 +389,7 @@ otherwise as Base64.`,
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil)
cobra.CheckErr(err)

var result contracts.CallResult
Expand Down Expand Up @@ -452,7 +452,7 @@ otherwise as Base64.`,
})

acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx)
sigTx, meta, err := common.SignParaTimeTransaction(ctx, npa, acc, conn, tx, nil)
cobra.CheckErr(err)

common.BroadcastTransaction(ctx, npa.ParaTime, conn, sigTx, meta, nil)
Expand Down Expand Up @@ -484,7 +484,7 @@ func parsePolicy(net *config.Network, wallet *cliConfig.Account, policy string)
return &contracts.Policy{Address: &address}
case strings.HasPrefix(policy, "address:"):
policy = strings.TrimPrefix(policy, "address:")
address, err := common.ResolveLocalAccountOrAddress(net, policy)
address, _, err := common.ResolveLocalAccountOrAddress(net, policy)
if err != nil {
cobra.CheckErr(fmt.Errorf("malformed address in policy: %w", err))
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/inspect/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func parseIdentifier(
return sel, nil
}

addr, err := helpers.ResolveAddress(npa.Network, s)
addr, _, err := helpers.ResolveAddress(npa.Network, s)
if err == nil {
return addr, nil
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var (
case *types.Transaction:
// Unsigned runtime transaction, sign first.
acc := common.LoadAccount(cfg, npa.AccountName)
sigTx, meta, err = common.SignParaTimeTransaction(ctx, npa, acc, conn, dtx)
sigTx, meta, err = common.SignParaTimeTransaction(ctx, npa, acc, conn, dtx, nil)
cobra.CheckErr(err)
}

Expand Down
2 changes: 1 addition & 1 deletion wallet/file/secp256k1.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const (
// privateKeySize is the length of Secp256k1 private key (32 bytes).
privateKeySize = 32

// Bip44DerivationPath is the derivation path defined by BIP-44.
// Bip44DerivationPath is the derivation path defined by BIP-44 for ETH coin type.
Bip44DerivationPath = "m/44'/60'/0'/0/%d"
)

Expand Down
52 changes: 43 additions & 9 deletions wallet/ledger/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ func getLegacyPath(number uint32) []uint32 {
return []uint32{44, 474, 0, 0, number}
}

func getBip44bytes(bip44Path []uint32) ([]byte, error) {
message := make([]byte, 4*len(bip44Path))
switch len(bip44Path) {
func getBip44Path(number uint32) []uint32 {
return []uint32{44, 60, 0, 0, number}
}

func getSerializedPath(path []uint32) ([]byte, error) {
message := make([]byte, 4*len(path))
switch len(path) {
case 5:
// Legacy derivation path.
case 3:
Expand All @@ -26,20 +30,42 @@ func getBip44bytes(bip44Path []uint32) ([]byte, error) {
return nil, fmt.Errorf("path should contain either 5 or 3 elements")
}

for index, element := range bip44Path {
for index, element := range path {
pos := index * 4
value := element | 0x80000000 // Harden all components.
binary.LittleEndian.PutUint32(message[pos:], value)
}
return message, nil
}

func prepareChunks(bip44PathBytes, context, message []byte, chunkSize int) ([][]byte, error) {
if len(context) > 255 {
return nil, fmt.Errorf("maximum supported context size is 255 bytes")
func getSerializedBip44Path(path []uint32) ([]byte, error) {
message := make([]byte, 4*len(path))
switch len(path) {
case 5:
// BIP-44 derivation path has always 5 elements.
default:
return nil, fmt.Errorf("path should contain 5 elements")
}

// First three elements are hardened
for index, element := range path[:3] {
pos := index * 4
value := element | 0x80000000 // Harden all components.
binary.LittleEndian.PutUint32(message[pos:], value)
}
return message, nil
}

body := append([]byte{byte(len(context))}, context...)
func prepareChunks(pathBytes, context, message []byte, chunkSize int, ctxLen bool) ([][]byte, error) {
var body []byte
if ctxLen {
if len(context) > 255 {
return nil, fmt.Errorf("maximum supported context size is 255 bytes")
}

body = []byte{byte(len(context))}
}
body = append(body, context...)
body = append(body, message...)

packetCount := 1 + len(body)/chunkSize
Expand All @@ -48,7 +74,7 @@ func prepareChunks(bip44PathBytes, context, message []byte, chunkSize int) ([][]
}

chunks := make([][]byte, 0, packetCount)
chunks = append(chunks, bip44PathBytes) // First chunk is path.
chunks = append(chunks, pathBytes) // First chunk is path.

r := bytes.NewReader(body)
readLoop:
Expand All @@ -71,3 +97,11 @@ readLoop:

return chunks, nil
}

func prepareConsensusChunks(pathBytes, context, message []byte, chunkSize int) ([][]byte, error) {
return prepareChunks(pathBytes, context, message, chunkSize, true)
}

func prepareRuntimeChunks(pathBytes, metadata, message []byte, chunkSize int) ([][]byte, error) {
return prepareChunks(pathBytes, metadata, message, chunkSize, false)
}
Loading

0 comments on commit fa874db

Please sign in to comment.