Skip to content

Commit

Permalink
Merge pull request anoma#11 from heliaxdev/feat/10/key-derivation
Browse files Browse the repository at this point in the history
feat/10 - Key Derivation
  • Loading branch information
jurevans authored Mar 16, 2022
2 parents d5d870a + 3928d5f commit 36cb4ec
Show file tree
Hide file tree
Showing 19 changed files with 1,304 additions and 233 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy-wallet-at-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

- name: build the site
working-directory: ./anoma-wallet
run: CI=false yarn build-with-craco
run: CI=false yarn build

- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1.2.3
Expand Down
6 changes: 4 additions & 2 deletions anoma-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ crate-type = ["cdylib", "rlib"]

[dependencies]
anoma = { git = "https://github.com/anoma/anoma", tag = "v0.4.0", features = ["rand", "ferveo-tpke"] }
bip39 = "1.0.1"
bip32 = "0.3.0"
bip0039 = "0.6.0"
borsh = "0.9.0"
ed25519-dalek = {version = "1.0.1", default-features = false, features = ["rand", "u64_backend", "serde"]}
serde-wasm-bindgen = "0.3.1"
Expand All @@ -28,10 +29,11 @@ wasm-bindgen = { version = "0.2.78", features = ["serde-serialize"] }
console_error_panic_hook = "0.1.7"
js-sys = "0.3.5"
chrono = "0.4.0"
bitcoin = "0.27.1"

[dependencies.web-sys]
version = "0.3"
features = [ "console", "FileReader", "Window" ]
features = [ "console" ]

[dev-dependencies]
wasm-bindgen-test = "0.3.13"
Expand Down
1 change: 1 addition & 0 deletions anoma-lib/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct Account(pub Transaction);

#[wasm_bindgen]
impl Account {
/// Initialize an account on the Ledger
pub fn init(
serialized_keypair: JsValue,
token: String,
Expand Down
1 change: 1 addition & 0 deletions anoma-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use wasm_bindgen::prelude::*;

pub mod transfer;
pub mod account;
pub mod wallet;
mod utils;
mod types;

Expand Down
129 changes: 129 additions & 0 deletions anoma-lib/src/wallet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use serde::{Serialize, Deserialize};
use bip32::{Prefix, XPrv};
use bitcoin::{
util::{address::Address, key::PrivateKey},
ecdsa::PublicKey,
network::constants::Network
};
use bip0039::{Mnemonic, Seed, Language};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
#[derive(Serialize, Deserialize)]
pub struct Wallet {
root_key: String,
seed: Vec<u8>,
phrase: String,
password: String,
}

#[wasm_bindgen]
#[derive(Serialize, Deserialize)]
pub struct DerivedAccount {
address: String,
wif: String,
private_key: Vec<u8>,
public_key: Vec<u8>,
secret: Vec<u8>,
public: Vec<u8>,
}

#[wasm_bindgen]
#[derive(Serialize, Deserialize)]
pub struct ExtendedKeys {
xpriv: String,
xpub: String,
}

#[wasm_bindgen]
impl Wallet {
pub fn new(
phrase: String,
password: String) -> Wallet {

let mnemonic = Mnemonic::from_phrase(&phrase, Language::English).unwrap();
let seed = Seed::new(&mnemonic, &password);
let seed: &[u8] = seed.as_bytes();

// BIP32 Root Key
let root_xprv = XPrv::new(&seed);
let root_xprv_str = root_xprv.unwrap().to_string(Prefix::XPRV).to_string();

Wallet {
phrase,
password,
seed: seed.to_vec(),
root_key: root_xprv_str,
}
}

/// Derive account from a seed and a path
pub fn derive(&self, path: String) -> DerivedAccount {
let seed: &[u8] = &self.seed;

// BIP32 Extended Private Key
let xprv = XPrv::derive_from_path(&seed, &path.parse().unwrap()).unwrap();

// BIP32 Extended Public Key
let xpub = xprv.public_key();

// Address - Public Key to p2pkh (compressed)
let pub_bytes: &[u8] = &xpub.public_key().clone().to_bytes().to_vec();
let pk = PublicKey::from_slice(pub_bytes);
let address = Address::p2pkh(&pk.unwrap(), Network::Bitcoin);

// Private Key to WIF (Wallet Import Format)
let prv_bytes: &[u8] = &xprv.private_key().clone().to_bytes().to_vec();
let prv = PrivateKey::from_slice(&prv_bytes, Network::Bitcoin).unwrap();
let key = prv.to_wif();

// ed25519 keypair
let secret = ed25519_dalek::SecretKey::from_bytes(prv_bytes)
.expect("Could not create secret from bytes");
let public = ed25519_dalek::PublicKey::from(&secret);

DerivedAccount {
address: address.to_string(),
wif: key.to_string(),
private_key: xprv.private_key().to_bytes().to_vec(),
public_key: xpub.public_key().to_bytes().to_vec(),
secret: secret.to_bytes().to_vec(),
public: public.to_bytes().to_vec(),
}
}

/// Get extended keys from path
pub fn get_extended_keys(&self, path: String) -> ExtendedKeys {
let seed: &[u8] = &self.seed;

// BIP32 Extended Private Key
let xprv = XPrv::derive_from_path(&seed, &path.parse().unwrap()).unwrap();
let xprv_str = xprv.to_string(Prefix::XPRV).to_string();

// BIP32 Extended Public Key
let xpub = xprv.public_key();
let xpub_str = xpub.to_string(Prefix::XPUB);

ExtendedKeys {
xpriv: xprv_str,
xpub: xpub_str,
}
}

/// Get serialized Wallet
pub fn serialize(&self) -> Result<JsValue, JsValue> {
Ok(JsValue::from_serde(&self).expect("Wallet should serialize correctly"))
}

/// Get serialized extended keys
pub fn extended_keys(&self, path: String) -> Result<JsValue, JsValue> {
let keys = &self.get_extended_keys(path);
Ok(JsValue::from_serde(&keys).unwrap())
}

/// Get serialized derived account
pub fn account(&self, path: String) -> Result<JsValue, JsValue> {
let keys = &self.derive(path);
Ok(JsValue::from_serde(&keys).unwrap())
}
}
12 changes: 6 additions & 6 deletions anoma-wallet/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,29 @@
"@cosmjs/tendermint-rpc": "^0.27.1",
"bn.js": "^5.2.0",
"borsh": "^0.7.0",
"bs58": "^5.0.0",
"buffer": "^6.0.3",
"framer-motion": "^6.2.8",
"path": "^0.12.7",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "6",
"react-scripts": "5.0.0",
"slip44": "^1.2.24",
"stream": "^0.0.2",
"styled-components": "^5.3.3",
"typescript": "^4.5.5",
"wasm-react-scripts": "^5.0.2",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "npx wasm-react-scripts start",
"start-with-craco": "craco start",
"start": "craco start",
"start:local": "REACT_APP_LOCAL=\"true\" yarn start",
"wasm:build": "wasm-pack build ../anoma-lib/ --out-dir ../anoma-wallet/src/lib/anoma --out-name anoma --target web",
"wasm:build:node": "wasm-pack build ../anoma-lib/ --out-dir ../anoma-wallet/src/lib/anoma --out-name anoma --target nodejs",
"build": "npx wasm-react-scripts build",
"build-with-craco": "craco build",
"build": "craco build",
"lint": "npx eslint src --ext .ts,.tsx",
"lint:fix": "yarn lint -- --fix",
"test": "yarn wasm:build:node && npx wasm-react-scripts test",
"test": "yarn wasm:build:node && npx create-react-scripts test",
"eject": "npx react-scripts eject"
},
"eslintConfig": {
Expand Down Expand Up @@ -67,6 +66,7 @@
"@types/react-dom": "^17.0.11",
"@types/styled-components": "^5.1.22",
"babel-plugin-styled-components": "^2.0.3",
"create-react-scripts": "^0.1.6",
"eslint": "^8.9.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.4.0",
Expand Down
1 change: 1 addition & 0 deletions anoma-wallet/src/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable max-len */
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { TopNavigation } from "./TopNavigation";
Expand Down
2 changes: 1 addition & 1 deletion anoma-wallet/src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { type Token, TokenType, Tokens } from "./tokens";
export { type TokenType, Tokens } from "./tokens";
export { TxResponse } from "./tx";
export { TxWasm, VpWasm } from "./wasm";
79 changes: 41 additions & 38 deletions anoma-wallet/src/constants/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,45 @@
/* eslint-disable max-len */
export enum TokenType {
XAN,
BTC,
ETH,
DOT,
}

export type Token = {
import { registeredCoinTypes, RegisteredCoinType } from "slip44";

export type TokenType = "BTC" | "LTC" | "ETH" | "DOT";

type TokenInfo = {
symbol: string;
name: string;
address: string;
type: number;
path: number;
coin: string;
url: string;
address?: string;
};

// TODO: We need to have token addresses for the livenet. Currently, only
// testnet token addresses are listed below:
export const Tokens: Record<TokenType, Token> = {
[TokenType.XAN]: {
symbol: "XAN",
name: "Anoma",
address:
"atest1v4ehgw36x3prswzxggunzv6pxqmnvdj9xvcyzvpsggeyvs3cg9qnywf589qnwvfsg5erg3fkl09rg5",
},
[TokenType.BTC]: {
symbol: "BTC",
name: "Bitcoin",
address:
"atest1v4ehgw36xdzryve5gsc52veeg5cnsv2yx5eygvp38qcrvd29xy6rys6p8yc5xvp4xfpy2v694wgwcp",
},
[TokenType.ETH]: {
symbol: "ETH",
name: "Ethereum",
address:
"atest1v4ehgw36xqmr2d3nx3ryvd2xxgmrq33j8qcns33sxezrgv6zxdzrydjrxveygd2yxumrsdpsf9jc2p",
},
[TokenType.DOT]: {
symbol: "DOT",
name: "Polkadot",
address:
"atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn",
},
type Tokens = {
[key: string]: TokenInfo;
};

// Declare symbols for tokens we support:
const SYMBOLS = ["BTC", "LTC", "ETH", "DOT"];

export const Tokens: Tokens = registeredCoinTypes
.filter(([, , symbol]) => {
return SYMBOLS.indexOf(`${symbol}`) > -1;
})
.reduce((tokens: Tokens, coinType: RegisteredCoinType) => {
const [type, path, symbol, coin, url] = coinType;

tokens[symbol as TokenType] = {
type,
path,
symbol,
coin,
url,
} as TokenInfo;

return tokens;
}, {});

// Map a few test addresses for now:
Tokens["BTC"].address =
"atest1v4ehgw36xdzryve5gsc52veeg5cnsv2yx5eygvp38qcrvd29xy6rys6p8yc5xvp4xfpy2v694wgwcp";
Tokens["ETH"].address =
"atest1v4ehgw36xqmr2d3nx3ryvd2xxgmrq33j8qcns33sxezrgv6zxdzrydjrxveygd2yxumrsdpsf9jc2p";
Tokens["DOT"].address =
"atest1v4ehgw36gg6nvs2zgfpyxsfjgc65yv6pxy6nwwfsxgungdzrggeyzv35gveyxsjyxymyz335hur2jn";
10 changes: 9 additions & 1 deletion anoma-wallet/src/lib/AnomaClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import init, { Address, Keypair, Transfer, Account } from "lib/anoma";
import init, { Address, Keypair, Transfer, Account, Wallet } from "lib/anoma";

class AnomaClient {
public memory: WebAssembly.Memory | null = null;
Expand All @@ -8,6 +8,7 @@ class AnomaClient {
public readonly keypair = Keypair;
public readonly transfer = Transfer;
public readonly account = Account;
public readonly wallet = Wallet;

public async init(): Promise<AnomaClient> {
const _init =
Expand All @@ -21,4 +22,11 @@ class AnomaClient {
}
}

// Alias types to avoid conflicts with classes
export type AddressType = Address;
export type KeypairType = Keypair;
export type TransferType = Transfer;
export type AccountType = Account;
export type WalletType = Wallet;

export default AnomaClient;
Loading

0 comments on commit 36cb4ec

Please sign in to comment.