Skip to content

Commit

Permalink
[wallet-ffi] wallet_create takes seed words for recovery
Browse files Browse the repository at this point in the history
Breaking change in wallet FFI, new argument in `wallet_create` to
specify optional seed words for recovery.

Also starts alpha quality NodeJS FFI wallet client for testing purposes.
  • Loading branch information
Byron Hambly committed Jun 9, 2021
1 parent c9fdeb3 commit 98dfed7
Show file tree
Hide file tree
Showing 16 changed files with 1,109 additions and 53 deletions.
6 changes: 6 additions & 0 deletions applications/ffi_client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
libtari_wallet_ffi.*
wallet/
bak/
recovered/
seeds.txt
recovery/
18 changes: 18 additions & 0 deletions applications/ffi_client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# NodeJS FFI Client

Still a work in progress.

## Install deps

- `npm install`

## Build FFI lib

- Build the FFI lib: `cargo build -p tari_wallet_ffi --release --lib`
- Copy the lib into this folder: `cp target/release/libtari_wallet_ffi.dylib /path/to/here`

_(.dylib for macOS, .so for Linux, .dll for windows)_

## Run

- `npm start` - runs index.js file
244 changes: 244 additions & 0 deletions applications/ffi_client/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// this is nasty
// ¯\_(ツ)_/¯

const lib = require("./lib");
const ref = require("ref-napi");
const ffi = require("ffi-napi");

const i32 = ref.types.int32;
const u8 = ref.types.uint8;
const u64 = ref.types.uint64;
const bool = ref.types.bool;

try {
let err = ref.alloc(i32);
// console.log(err);

console.log("Create Tor transport...");
let tor = lib.transport_tor_create(
"/ip4/127.0.0.1/tcp/9051",
ref.NULL,
9051,
ref.NULL,
ref.NULL,
err
);

// todo: error handling

console.log("Create Comms config...");
let comms = lib.comms_config_create(
"/ip4/0.0.0.0/tcp/9838",
tor,
"wallet.dat",
"./wallet",
30,
600,
err
);

// callback_received_transaction: unsafe extern "C" fn(*mut TariPendingInboundTransaction),
const receivedTx = ffi.Callback("void", ["pointer"], function (ptr) {
console.log("receivedTx: ", ptr);
});
// callback_received_transaction_reply: unsafe extern "C" fn(*mut TariCompletedTransaction),
const receivedTxReply = ffi.Callback("void", ["pointer"], function (ptr) {
console.log("receivedTxReply: ", ptr);
});
// callback_received_finalized_transaction: unsafe extern "C" fn(*mut TariCompletedTransaction),
const receivedFinalized = ffi.Callback("void", ["pointer"], function (ptr) {
console.log("receivedFinalized: ", ptr);
});
// callback_transaction_broadcast: unsafe extern "C" fn(*mut TariCompletedTransaction),
const txBroadcast = ffi.Callback("void", ["pointer"], function (ptr) {
console.log("txBroadcast: ", ptr);
});
// callback_transaction_mined: unsafe extern "C" fn(*mut TariCompletedTransaction),
const txMined = ffi.Callback("void", ["pointer"], function (ptr) {
console.log("txMined: ", ptr);
});
// callback_transaction_mined_unconfirmed: unsafe extern "C" fn(*mut TariCompletedTransaction, u64),
const txMinedUnconfirmed = ffi.Callback(
"void",
["pointer"],
function (ptr, confirmations) {
console.log("txMinedUnconfirmed: ", ptr, confirmations);
}
);
// callback_direct_send_result: unsafe extern "C" fn(c_ulonglong, bool),
const directSendResult = ffi.Callback("void", [u64, bool], function (i, j) {
console.log("directSendResult: ", i, j);
});
// callback_store_and_forward_send_result: unsafe extern "C" fn(c_ulonglong, bool),
const safResult = ffi.Callback("void", [u64, bool], function (i, j) {
console.log("safResult: ", i, j);
});
// callback_transaction_cancellation: unsafe extern "C" fn(*mut TariCompletedTransaction),
const txCancelled = ffi.Callback("void", ["pointer"], function (ptr) {
console.log("txCancelled: ", ptr);
});
// callback_utxo_validation_complete: unsafe extern "C" fn(u64, u8),
const utxoValidation = ffi.Callback("void", [u64, u8], function (i, j) {
console.log("utxoValidation: ", i, j);
});
// callback_stxo_validation_complete: unsafe extern "C" fn(u64, u8),
const stxoValidation = ffi.Callback("void", [u64, u8], function (i, j) {
console.log("stxoValidation: ", i, j);
});
// callback_invalid_txo_validation_complete: unsafe extern "C" fn(u64, u8),
const itxoValidation = ffi.Callback("void", [u64, u8], function (i, j) {
console.log("itxoValidation: ", i, j);
});
// callback_transaction_validation_complete: unsafe extern "C" fn(u64, u8),
const txValidation = ffi.Callback("void", [u64, u8], function (i, j) {
console.log("txValidation: ", i, j);
});
// callback_saf_messages_received: unsafe extern "C" fn(),
const safsReceived = ffi.Callback("void", [], function () {
console.log("safsReceived");
});

console.log("Create Wallet...");
let wallet = lib.wallet_create(
comms,
"./wallet/logs/wallet.log",
5,
10240,
ref.NULL, // passphrase
ref.NULL, // seed words
receivedTx,
receivedTxReply,
receivedFinalized,
txBroadcast,
txMined,
txMinedUnconfirmed,
directSendResult,
safResult,
txCancelled,
utxoValidation,
stxoValidation,
itxoValidation,
txValidation,
safsReceived,
err
);

// look ma, zero confs!
lib.wallet_set_num_confirmations_required(wallet, 0, err);
// console.log(err.deref());
let confs = lib.wallet_get_num_confirmations_required(wallet, err);
// console.log("confs", confs);
// console.log(err.deref());

let s = lib.wallet_get_seed_words(wallet, err);
// console.log("seeds words", s);
// console.log("err", err);
let seedWords = [];
for (let i = 0; i < 24; i++) {
let word = lib.seed_words_get_at(s, i, err);
// console.log("word", word);
// console.log("err", err.deref());
seedWords.push(word);
}
console.log("seedWords", seedWords);

function getBalance() {
try {
console.log("===");
console.log("Balance");
console.log("===");
let available = lib.wallet_get_available_balance(wallet, err);
console.log(" available : ", available);
let pending_in = lib.wallet_get_pending_incoming_balance(wallet, err);
console.log(" pending_in: ", pending_in);
console.log("===");
} catch (e) {
console.error("balance error: ", e);
}
}
let j = setInterval(getBalance, 10000);

const u8ArrayFromHex = (hexString) =>
new Uint8Array(
hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
);
const u8ArrayToHex = (bytes) =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");

// let myPublicKey = lib.wallet_get_public_key(wallet, err);
// console.log(myPublicKey);
// console.log(err);
// let temp = lib.public_key_get_bytes(myPublicKey, err);
// console.log("temp", temp.deref());

// process.exit();

let publicKeyHex =
"0c3fe3c23866ed3827e1cd72aae0c9d364d860d597993104e90d9a9401e52f05";
let publicKeyBytes = u8ArrayFromHex(publicKeyHex);
let publicKeyByteVector = lib.byte_vector_create(publicKeyBytes, 32, err);
let publicKey = lib.public_key_create(publicKeyByteVector, err);

console.log("Set base node peer...", publicKeyHex);
lib.wallet_add_base_node_peer(
wallet,
publicKey,
"/onion3/2m2xnylrsqbaozsndkbmfisxxbwh2vgvs6oyfak2qah4snnxykrf7zad:18141",
err
);

setTimeout(function () {
try {
console.log("start tx validation");
let id = lib.wallet_start_transaction_validation(wallet, err);
console.log("tx validation request id", id);

console.log("start utxo validation");
id = lib.wallet_start_utxo_validation(wallet, err);
console.log("utxo validation request id", id);
} catch (e) {
console.error("validation error: ", e);
}
}, 5000);

console.log("Wallet running...");
console.log("Ctrl+C to quit.");
process.stdin.resume();

function exitHandler(options, exitCode) {
try {
console.log("exitHandler");
console.log("options", options);
console.log("exitCode", exitCode);
if (options.cleanup) {
console.log("Exiting...");
lib.wallet_destroy(wallet);
console.log("Goodbye.");
}
if (exitCode || exitCode === 0) console.log("\nExit code:", exitCode);
if (options.exit) process.exit();
} catch (e) {
console.error("exitHandler error", e);
}
}

process.on("exit", exitHandler.bind(null, { cleanup: true, signal: "exit" }));
process.on(
"SIGINT",
exitHandler.bind(null, { exit: true, signal: "SIGINT" })
);
process.on(
"SIGUSR1",
exitHandler.bind(null, { exit: true, signal: "SIGUSR1" })
);
process.on(
"SIGUSR2",
exitHandler.bind(null, { exit: true, signal: "SIGUSR2" })
);
process.on(
"uncaughtException",
exitHandler.bind(null, { exit: true, signal: "uncaughtException" })
);
} catch (e) {
console.error("ERROR: ", e);
}
81 changes: 81 additions & 0 deletions applications/ffi_client/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const ffi = require("ffi-napi");

const {
strPtr,
errPtr,
transportRef,
commsConfigRef,
walletRef,
fn,
bool,
u8,
u16,
i32,
u32,
u64,
u8Array,
u8ArrayPtr,
byteVectorRef,
publicKeyRef,
strArray,
strArrayPtr,
} = require("./types");

// todo: check if the lib exists first

console.log("Set up library...");
const libWallet = ffi.Library("./libtari_wallet_ffi.dylib", {
byte_vector_create: [byteVectorRef, [u8ArrayPtr, u32, errPtr]],
comms_config_create: [
commsConfigRef,
["string", transportRef, "string", "string", u64, u64, errPtr],
],
public_key_create: [publicKeyRef, [byteVectorRef, errPtr]],
public_key_get_bytes: [u8ArrayPtr, [publicKeyRef, errPtr]],
seed_words_create: [strPtr, []],
seed_words_get_at: ["string", [strArrayPtr, u32, errPtr]],
seed_words_push_word: [u8, [strPtr, "string", errPtr]],
transport_tor_create: [
transportRef,
["string", u8ArrayPtr, u16, "string", "string", errPtr],
],
wallet_add_base_node_peer: [bool, [walletRef, u8ArrayPtr, "string", errPtr]],
wallet_create: [
walletRef,
[
commsConfigRef,
"string",
u32,
u32,
"string",
"string",
fn,
fn,
fn,
fn,
fn,
fn,
fn,
fn,
fn,
fn,
fn,
fn,
fn,
fn,
errPtr,
],
],
wallet_destroy: ["void", [walletRef]],
wallet_get_available_balance: [u64, [walletRef, errPtr]],
wallet_get_pending_incoming_balance: [u64, [walletRef, errPtr]],
wallet_get_public_key: [publicKeyRef, [walletRef, errPtr]],
wallet_get_seed_words: [strArrayPtr, [walletRef, errPtr]],
wallet_get_num_confirmations_required: [u64, [walletRef, errPtr]],
wallet_set_num_confirmations_required: ["void", [walletRef, u64, errPtr]],
wallet_start_transaction_validation: [u64, [walletRef, errPtr]],
wallet_start_utxo_validation: [u64, [walletRef, errPtr]],
wallet_start_recovery: [bool, [walletRef, publicKeyRef, fn, errPtr]],
});

module.exports = libWallet;
42 changes: 42 additions & 0 deletions applications/ffi_client/lib/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const ref = require("ref-napi");
const ArrayType = require("ref-array-napi");

const strPtr = ref.refType(ref.types.CString);
const errPtr = ref.refType(ref.types.int32);
const transportRef = ref.refType(ref.types.void);
const commsConfigRef = ref.refType(ref.types.void);
const walletRef = ref.refType(ref.types.void);
const fn = ref.refType(ref.types.void);
const bool = ref.types.bool;
const u8 = ref.types.uint8;
const u16 = ref.types.uint16;
const i32 = ref.types.int32;
const u32 = ref.types.uint32;
const u64 = ref.types.uint64;
const u8Array = ArrayType(ref.types.uint8);
const u8ArrayPtr = ref.refType(u8Array);
const byteVectorRef = ref.refType(u8Array);
const publicKeyRef = ref.refType(ref.types.void);
const strArray = ArrayType("string");
const strArrayPtr = ref.refType(ArrayType("string"));

module.exports = {
strPtr,
errPtr,
transportRef,
commsConfigRef,
walletRef,
fn,
bool,
u8,
u16,
i32,
u32,
u64,
u8Array,
u8ArrayPtr,
byteVectorRef,
publicKeyRef,
strArray,
strArrayPtr,
};
Loading

0 comments on commit 98dfed7

Please sign in to comment.