Skip to content

Commit

Permalink
Merge pull request #20 from ava-labs/add-key
Browse files Browse the repository at this point in the history
add missing key
  • Loading branch information
felipemadero authored May 28, 2024
2 parents aedef85 + 342578d commit 2da2a9a
Show file tree
Hide file tree
Showing 4 changed files with 689 additions and 0 deletions.
116 changes: 116 additions & 0 deletions key/key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// Package key implements key manager and helper functions.
package key

import (
"bytes"
"errors"
"sort"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/platformvm/txs"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
)

var (
ErrInvalidType = errors.New("invalid type")
ErrCantSpend = errors.New("can't spend")
)

// Key defines methods for key manager interface.
type Key interface {
// P returns all formatted P-Chain addresses.
P(string) (string, error)
// C returns the C-Chain address in Ethereum format
C() string
// Addresses returns the all raw ids.ShortID address.
Addresses() []ids.ShortID
// Match attempts to match a list of addresses up to the provided threshold.
Match(owners *secp256k1fx.OutputOwners, time uint64) ([]uint32, []ids.ShortID, bool)
// Spend attempts to spend all specified UTXOs (outputs)
// and returns the new UTXO inputs.
//
// If target amount is specified, it only uses the
// outputs until the total spending is below the target
// amount.
Spends(outputs []*avax.UTXO, opts ...OpOption) (
totalBalanceToSpend uint64,
inputs []*avax.TransferableInput,
signers [][]ids.ShortID,
)
// Sign generates [numSigs] signatures and attaches them to [pTx].
Sign(pTx *txs.Tx, signers [][]ids.ShortID) error
}

type Op struct {
time uint64
targetAmount uint64
feeDeduct uint64
}

type OpOption func(*Op)

func (op *Op) applyOpts(opts []OpOption) {
for _, opt := range opts {
opt(op)
}
}

func WithTime(t uint64) OpOption {
return func(op *Op) {
op.time = t
}
}

func WithTargetAmount(ta uint64) OpOption {
return func(op *Op) {
op.targetAmount = ta
}
}

// To deduct transfer fee from total spend (output).
// e.g., "units.MilliAvax" for X/P-Chain transfer.
func WithFeeDeduct(fee uint64) OpOption {
return func(op *Op) {
op.feeDeduct = fee
}
}

type innerSortTransferableInputsWithSigners struct {
ins []*avax.TransferableInput
signers [][]ids.ShortID
}

func (ins *innerSortTransferableInputsWithSigners) Less(i, j int) bool {
iID, iIndex := ins.ins[i].InputSource()
jID, jIndex := ins.ins[j].InputSource()

switch bytes.Compare(iID[:], jID[:]) {
case -1:
return true
case 0:
return iIndex < jIndex
default:
return false
}
}

func (ins *innerSortTransferableInputsWithSigners) Len() int {
return len(ins.ins)
}

func (ins *innerSortTransferableInputsWithSigners) Swap(i, j int) {
ins.ins[j], ins.ins[i] = ins.ins[i], ins.ins[j]
ins.signers[j], ins.signers[i] = ins.signers[i], ins.signers[j]
}

// SortTransferableInputsWithSigners sorts the inputs and signers based on the
// input's utxo ID.
//
// This is based off of (generics?): https://github.com/ava-labs/avalanchego/blob/224c9fd23d41839201dd0275ac864a845de6e93e/vms/components/avax/transferables.go#L202
func SortTransferableInputsWithSigners(ins []*avax.TransferableInput, signers [][]ids.ShortID) {
sort.Sort(&innerSortTransferableInputsWithSigners{ins: ins, signers: signers})
}
115 changes: 115 additions & 0 deletions key/key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package key

import (
"bytes"
"errors"
"path/filepath"
"testing"

"github.com/ava-labs/avalanchego/utils/cb58"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
)

const ewoqPChainAddr = "P-custom18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p"

func TestNewKeyEwoq(t *testing.T) {
t.Parallel()

m, err := NewSoft(
WithPrivateKeyEncoded(EwoqPrivateKey),
)
if err != nil {
t.Fatal(err)
}

pAddr, err := m.P("custom")
if err != nil {
t.Fatal(err)
}
if pAddr != ewoqPChainAddr {
t.Fatalf("unexpected P-Chain address %q, expected %q", pAddr, ewoqPChainAddr)
}

keyPath := filepath.Join(t.TempDir(), "key.pk")
if err := m.Save(keyPath); err != nil {
t.Fatal(err)
}

m2, err := LoadSoft(keyPath)
if err != nil {
t.Fatal(err)
}

if !bytes.Equal(m.PrivKeyRaw(), m2.PrivKeyRaw()) {
t.Fatalf("loaded key unexpected %v, expected %v", m2.PrivKeyRaw(), m.PrivKeyRaw())
}
}

func TestNewKey(t *testing.T) {
t.Parallel()

skBytes, err := cb58.Decode(rawEwoqPk)
if err != nil {
t.Fatal(err)
}
ewoqPk, err := secp256k1.ToPrivateKey(skBytes)
if err != nil {
t.Fatal(err)
}

privKey2, err := secp256k1.NewPrivateKey()
if err != nil {
t.Fatal(err)
}

tt := []struct {
name string
opts []SOpOption
expErr error
}{
{
name: "test",
opts: nil,
expErr: nil,
},
{
name: "ewop with WithPrivateKey",
opts: []SOpOption{
WithPrivateKey(ewoqPk),
},
expErr: nil,
},
{
name: "ewop with WithPrivateKeyEncoded",
opts: []SOpOption{
WithPrivateKeyEncoded(EwoqPrivateKey),
},
expErr: nil,
},
{
name: "ewop with WithPrivateKey/WithPrivateKeyEncoded",
opts: []SOpOption{
WithPrivateKey(ewoqPk),
WithPrivateKeyEncoded(EwoqPrivateKey),
},
expErr: nil,
},
{
name: "ewop with invalid WithPrivateKey",
opts: []SOpOption{
WithPrivateKey(privKey2),
WithPrivateKeyEncoded(EwoqPrivateKey),
},
expErr: ErrInvalidPrivateKey,
},
}
for i, tv := range tt {
_, err := NewSoft(tv.opts...)
if !errors.Is(err, tv.expErr) {
t.Fatalf("#%d(%s): unexpected error %v, expected %v", i, tv.name, err, tv.expErr)
}
}
}
78 changes: 78 additions & 0 deletions key/ledger_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (C) 2019-2022, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package key

import (
"fmt"

"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/platformvm/txs"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
)

var _ Key = &LedgerKey{}

type LedgerKey struct {
index uint32
}

// ledger device should be connected
func NewLedger(index uint32) LedgerKey {
return LedgerKey{
index: index,
}
}

// LoadLedger loads the ledger key info from disk and creates the corresponding LedgerKey.
func LoadLedger(_ string) (*LedgerKey, error) {
return nil, fmt.Errorf("not implemented")
}

// LoadLedgerFromBytes loads the ledger key info from bytes and creates the corresponding LedgerKey.
func LoadLedgerFromBytes(_ []byte) (*SoftKey, error) {
return nil, fmt.Errorf("not implemented")
}

func (*LedgerKey) C() string {
return ""
}

// Returns the KeyChain
func (*LedgerKey) KeyChain() *secp256k1fx.Keychain {
return nil
}

// Saves the key info to disk
func (*LedgerKey) Save(_ string) error {
return fmt.Errorf("not implemented")
}

func (*LedgerKey) P(_ string) (string, error) {
return "", fmt.Errorf("not implemented")
}

func (*LedgerKey) X(_ string) (string, error) {
return "", fmt.Errorf("not implemented")
}

func (*LedgerKey) Spends(_ []*avax.UTXO, _ ...OpOption) (
totalBalanceToSpend uint64,
inputs []*avax.TransferableInput,
signers [][]ids.ShortID,
) {
return 0, nil, nil
}

func (*LedgerKey) Addresses() []ids.ShortID {
return nil
}

func (*LedgerKey) Sign(_ *txs.Tx, _ [][]ids.ShortID) error {
return fmt.Errorf("not implemented")
}

func (*LedgerKey) Match(_ *secp256k1fx.OutputOwners, _ uint64) ([]uint32, []ids.ShortID, bool) {
return nil, nil, false
}
Loading

0 comments on commit 2da2a9a

Please sign in to comment.