Feature: Use napi-rs instead of neon
This commit is contained in:
2
backend/rust-gbt/.gitignore
vendored
2
backend/rust-gbt/.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
index.node
|
||||
*.node
|
||||
**/node_modules
|
||||
**/.DS_Store
|
||||
npm-debug.log*
|
||||
|
||||
@@ -15,8 +15,8 @@ crate-type = ["cdylib"]
|
||||
priority-queue = "1.3.2"
|
||||
bytes = "1.4.0"
|
||||
once_cell = "1.18.0"
|
||||
napi = { version = "2.13.2", features = ["napi8"] }
|
||||
napi-derive = "2.13.0"
|
||||
|
||||
[dependencies.neon]
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = ["napi-6","channel-api"]
|
||||
[build-dependencies]
|
||||
napi-build = "2.0.1"
|
||||
|
||||
3
backend/rust-gbt/build.rs
Normal file
3
backend/rust-gbt/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
napi_build::setup();
|
||||
}
|
||||
13
backend/rust-gbt/index.d.ts
vendored
Normal file
13
backend/rust-gbt/index.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
/* auto-generated by NAPI-RS */
|
||||
|
||||
export function make(mempoolBuffer: Uint8Array, callback: (arg0: GbtResult) => void): void
|
||||
export function update(newTxs: Uint8Array, removeTxs: Uint8Array, callback: (arg0: GbtResult) => void): void
|
||||
export class GbtResult {
|
||||
blocks: Array<Array<number>>
|
||||
clusters: Array<Array<number>>
|
||||
rates: Array<Array<number>>
|
||||
constructor(blocks: Array<Array<number>>, clusters: Array<Array<number>>, rates: Array<Array<number>>)
|
||||
}
|
||||
259
backend/rust-gbt/index.js
Normal file
259
backend/rust-gbt/index.js
Normal file
@@ -0,0 +1,259 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
|
||||
/* auto-generated by NAPI-RS */
|
||||
|
||||
const { existsSync, readFileSync } = require('fs')
|
||||
const { join } = require('path')
|
||||
|
||||
const { platform, arch } = process
|
||||
|
||||
let nativeBinding = null
|
||||
let localFileExisted = false
|
||||
let loadError = null
|
||||
|
||||
function isMusl() {
|
||||
// For Node 10
|
||||
if (!process.report || typeof process.report.getReport !== 'function') {
|
||||
try {
|
||||
const lddPath = require('child_process').execSync('which ldd').toString().trim()
|
||||
return readFileSync(lddPath, 'utf8').includes('musl')
|
||||
} catch (e) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
const { glibcVersionRuntime } = process.report.getReport().header
|
||||
return !glibcVersionRuntime
|
||||
}
|
||||
}
|
||||
|
||||
switch (platform) {
|
||||
case 'android':
|
||||
switch (arch) {
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(join(__dirname, 'gbt.android-arm64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.android-arm64.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-android-arm64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm':
|
||||
localFileExisted = existsSync(join(__dirname, 'gbt.android-arm-eabi.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.android-arm-eabi.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-android-arm-eabi')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Android ${arch}`)
|
||||
}
|
||||
break
|
||||
case 'win32':
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.win32-x64-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.win32-x64-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-win32-x64-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'ia32':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.win32-ia32-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.win32-ia32-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-win32-ia32-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.win32-arm64-msvc.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.win32-arm64-msvc.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-win32-arm64-msvc')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Windows: ${arch}`)
|
||||
}
|
||||
break
|
||||
case 'darwin':
|
||||
localFileExisted = existsSync(join(__dirname, 'gbt.darwin-universal.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.darwin-universal.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-darwin-universal')
|
||||
}
|
||||
break
|
||||
} catch {}
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
localFileExisted = existsSync(join(__dirname, 'gbt.darwin-x64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.darwin-x64.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-darwin-x64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.darwin-arm64.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.darwin-arm64.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-darwin-arm64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on macOS: ${arch}`)
|
||||
}
|
||||
break
|
||||
case 'freebsd':
|
||||
if (arch !== 'x64') {
|
||||
throw new Error(`Unsupported architecture on FreeBSD: ${arch}`)
|
||||
}
|
||||
localFileExisted = existsSync(join(__dirname, 'gbt.freebsd-x64.node'))
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.freebsd-x64.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-freebsd-x64')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
case 'linux':
|
||||
switch (arch) {
|
||||
case 'x64':
|
||||
if (isMusl()) {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.linux-x64-musl.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.linux-x64-musl.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-linux-x64-musl')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
} else {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.linux-x64-gnu.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.linux-x64-gnu.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-linux-x64-gnu')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'arm64':
|
||||
if (isMusl()) {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.linux-arm64-musl.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.linux-arm64-musl.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-linux-arm64-musl')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
} else {
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.linux-arm64-gnu.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.linux-arm64-gnu.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-linux-arm64-gnu')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'arm':
|
||||
localFileExisted = existsSync(
|
||||
join(__dirname, 'gbt.linux-arm-gnueabihf.node')
|
||||
)
|
||||
try {
|
||||
if (localFileExisted) {
|
||||
nativeBinding = require('./gbt.linux-arm-gnueabihf.node')
|
||||
} else {
|
||||
nativeBinding = require('gbt-linux-arm-gnueabihf')
|
||||
}
|
||||
} catch (e) {
|
||||
loadError = e
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported architecture on Linux: ${arch}`)
|
||||
}
|
||||
break
|
||||
default:
|
||||
throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`)
|
||||
}
|
||||
|
||||
if (!nativeBinding) {
|
||||
if (loadError) {
|
||||
throw loadError
|
||||
}
|
||||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
const { make, update, GbtResult } = nativeBinding
|
||||
|
||||
module.exports.make = make
|
||||
module.exports.update = update
|
||||
module.exports.GbtResult = GbtResult
|
||||
22
backend/rust-gbt/package-lock.json
generated
22
backend/rust-gbt/package-lock.json
generated
@@ -9,16 +9,26 @@
|
||||
"version": "0.1.0",
|
||||
"hasInstallScript": true,
|
||||
"devDependencies": {
|
||||
"cargo-cp-artifact": "^0.1"
|
||||
"@napi-rs/cli": "^2.16.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/cargo-cp-artifact": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/cargo-cp-artifact/-/cargo-cp-artifact-0.1.8.tgz",
|
||||
"integrity": "sha512-3j4DaoTrsCD1MRkTF2Soacii0Nx7UHCce0EwUf4fHnggwiE4fbmF2AbnfzayR36DF8KGadfh7M/Yfy625kgPlA==",
|
||||
"node_modules/@napi-rs/cli": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.16.1.tgz",
|
||||
"integrity": "sha512-L0Gr5iEQIDEbvWdDr1HUaBOxBSHL1VZhWSk1oryawoT8qJIY+KGfLFelU+Qma64ivCPbxYpkfPoKYVG3rcoGIA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"cargo-cp-artifact": "bin/cargo-cp-artifact.js"
|
||||
"napi": "scripts/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/Brooooooklyn"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,32 @@
|
||||
"name": "gbt",
|
||||
"version": "0.1.0",
|
||||
"description": "An inefficient re-implementation of the getBlockTemplate algorithm in Rust",
|
||||
"main": "index.node",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
"build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
|
||||
"artifacts": "napi artifacts",
|
||||
"build": "napi build --platform",
|
||||
"build-debug": "npm run build --",
|
||||
"build-release": "npm run build -- --release",
|
||||
"install": "npm run build-release",
|
||||
"prepublishOnly": "napi prepublish -t npm",
|
||||
"test": "cargo test"
|
||||
},
|
||||
"author": "mononaut",
|
||||
"napi": {
|
||||
"name": "gbt",
|
||||
"triples": {
|
||||
"defaults": false,
|
||||
"additional": [
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-unknown-freebsd"
|
||||
]
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"cargo-cp-artifact": "^0.1"
|
||||
"@napi-rs/cli": "^2.16.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
use priority_queue::PriorityQueue;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
};
|
||||
|
||||
use crate::audit_transaction::AuditTransaction;
|
||||
use crate::thread_transaction::ThreadTransaction;
|
||||
use crate::{
|
||||
audit_transaction::AuditTransaction, thread_transaction::ThreadTransaction, GbtResult,
|
||||
};
|
||||
|
||||
const BLOCK_WEIGHT_UNITS: u32 = 4_000_000;
|
||||
const BLOCK_SIGOPS: u32 = 80_000;
|
||||
@@ -35,14 +38,6 @@ impl Ord for TxPriority {
|
||||
}
|
||||
}
|
||||
|
||||
/// The result from calling the gbt function.
|
||||
///
|
||||
/// This tuple contains the following:
|
||||
/// 1. A 2D Vector of transaction IDs (u32), the inner Vecs each represent a block.
|
||||
/// 2. A Vector of tuples containing transaction IDs (u32) and effective fee per vsize (f64)
|
||||
/// 3. A 2D Vector of transaction IDs representing clusters of dependent mempool transactions
|
||||
pub type GbtResult = (Vec<Vec<u32>>, Vec<(u32, f64)>, Vec<Vec<u32>>);
|
||||
|
||||
/*
|
||||
* Build projected mempool blocks using an approximation of the transaction selection algorithm from Bitcoin Core
|
||||
* (see BlockAssembler in https://github.com/bitcoin/bitcoin/blob/master/src/node/miner.cpp)
|
||||
@@ -51,7 +46,7 @@ pub type GbtResult = (Vec<Vec<u32>>, Vec<(u32, f64)>, Vec<Vec<u32>>);
|
||||
pub fn gbt(mempool: &mut HashMap<u32, ThreadTransaction>) -> Option<GbtResult> {
|
||||
let mut audit_pool: HashMap<u32, AuditTransaction> = HashMap::new();
|
||||
let mut mempool_array: VecDeque<u32> = VecDeque::new();
|
||||
let mut cluster_array: Vec<Vec<u32>> = Vec::new();
|
||||
let mut clusters: Vec<Vec<u32>> = Vec::new();
|
||||
|
||||
// Initialize working structs
|
||||
for (uid, tx) in mempool {
|
||||
@@ -130,7 +125,7 @@ pub fn gbt(mempool: &mut HashMap<u32, ThreadTransaction>) -> Option<GbtResult> {
|
||||
package.sort_unstable_by_key(|a| 0 - a.1);
|
||||
|
||||
if is_cluster {
|
||||
cluster_array.push(cluster);
|
||||
clusters.push(cluster);
|
||||
}
|
||||
|
||||
let cluster_rate = next_tx
|
||||
@@ -197,14 +192,18 @@ pub fn gbt(mempool: &mut HashMap<u32, ThreadTransaction>) -> Option<GbtResult> {
|
||||
}
|
||||
|
||||
// make a list of dirty transactions and their new rates
|
||||
let mut rates: Vec<(u32, f64)> = Vec::new();
|
||||
let mut rates: Vec<Vec<f64>> = Vec::new();
|
||||
for (txid, tx) in audit_pool {
|
||||
if tx.dirty {
|
||||
rates.push((txid, tx.effective_fee_per_vsize));
|
||||
rates.push(vec![txid as f64, tx.effective_fee_per_vsize]);
|
||||
}
|
||||
}
|
||||
|
||||
Some((blocks, rates, cluster_array))
|
||||
Some(GbtResult {
|
||||
blocks,
|
||||
rates,
|
||||
clusters,
|
||||
})
|
||||
}
|
||||
|
||||
fn set_relatives(txid: u32, audit_pool: &mut HashMap<u32, AuditTransaction>) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use neon::{prelude::*, types::buffer::TypedArray};
|
||||
use napi::bindgen_prelude::*;
|
||||
use napi_derive::napi;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
@@ -12,115 +14,66 @@ use thread_transaction::ThreadTransaction;
|
||||
static THREAD_TRANSACTIONS: Lazy<Mutex<HashMap<u32, ThreadTransaction>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
fn make(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
let mempool_arg = cx
|
||||
.argument::<JsArrayBuffer>(0)?
|
||||
.root(&mut cx)
|
||||
.into_inner(&mut cx);
|
||||
let callback = cx.argument::<JsFunction>(1)?.root(&mut cx);
|
||||
let channel = cx.channel();
|
||||
|
||||
let buffer = mempool_arg.as_slice(&cx);
|
||||
|
||||
#[napi]
|
||||
pub fn make<F>(mempool_buffer: Uint8Array, callback: F) -> Result<()>
|
||||
where
|
||||
F: Fn(GbtResult) -> Result<()>,
|
||||
{
|
||||
let mut map = HashMap::new();
|
||||
for tx in ThreadTransaction::batch_from_buffer(buffer) {
|
||||
for tx in ThreadTransaction::batch_from_buffer(&mempool_buffer) {
|
||||
map.insert(tx.uid, tx);
|
||||
}
|
||||
|
||||
let mut global_map = THREAD_TRANSACTIONS.lock().unwrap();
|
||||
*global_map = map;
|
||||
|
||||
run_in_thread(channel, callback);
|
||||
|
||||
Ok(cx.undefined())
|
||||
run_in_thread(callback)
|
||||
}
|
||||
|
||||
fn update(mut cx: FunctionContext) -> JsResult<JsUndefined> {
|
||||
let new_txs_arg = cx
|
||||
.argument::<JsArrayBuffer>(0)?
|
||||
.root(&mut cx)
|
||||
.into_inner(&mut cx);
|
||||
let remove_txs_arg = cx
|
||||
.argument::<JsArrayBuffer>(1)?
|
||||
.root(&mut cx)
|
||||
.into_inner(&mut cx);
|
||||
let callback = cx.argument::<JsFunction>(2)?.root(&mut cx);
|
||||
let channel = cx.channel();
|
||||
|
||||
#[napi]
|
||||
pub fn update<F>(new_txs: Uint8Array, remove_txs: Uint8Array, callback: F) -> Result<()>
|
||||
where
|
||||
F: Fn(GbtResult) -> Result<()>,
|
||||
{
|
||||
let mut map = THREAD_TRANSACTIONS.lock().unwrap();
|
||||
let new_tx_buffer = new_txs_arg.as_slice(&cx);
|
||||
for tx in ThreadTransaction::batch_from_buffer(new_tx_buffer) {
|
||||
for tx in ThreadTransaction::batch_from_buffer(&new_txs) {
|
||||
map.insert(tx.uid, tx);
|
||||
}
|
||||
|
||||
let remove_tx_buffer = remove_txs_arg.as_slice(&cx);
|
||||
for txid in &utils::txids_from_buffer(remove_tx_buffer) {
|
||||
for txid in &utils::txids_from_buffer(&remove_txs) {
|
||||
map.remove(txid);
|
||||
}
|
||||
drop(map);
|
||||
|
||||
run_in_thread(channel, callback);
|
||||
|
||||
Ok(cx.undefined())
|
||||
run_in_thread(callback)
|
||||
}
|
||||
|
||||
fn run_in_thread(channel: Channel, callback: Root<JsFunction>) {
|
||||
std::thread::spawn(move || {
|
||||
let mut map = THREAD_TRANSACTIONS.lock().unwrap();
|
||||
let (blocks, rates, clusters) = gbt::gbt(&mut map).unwrap();
|
||||
drop(map);
|
||||
/// The result from calling the gbt function.
|
||||
///
|
||||
/// This tuple contains the following:
|
||||
/// blocks: A 2D Vector of transaction IDs (u32), the inner Vecs each represent a block.
|
||||
/// clusters: A 2D Vector of transaction IDs representing clusters of dependent mempool transactions
|
||||
/// rates: A Vector of tuples containing transaction IDs (u32) and effective fee per vsize (f64)
|
||||
#[napi(constructor)]
|
||||
pub struct GbtResult {
|
||||
pub blocks: Vec<Vec<u32>>,
|
||||
pub clusters: Vec<Vec<u32>>,
|
||||
pub rates: Vec<Vec<f64>>, // Tuples not supported. u32 fits inside f64
|
||||
}
|
||||
|
||||
channel.send(move |mut cx| {
|
||||
let result = JsObject::new(&mut cx);
|
||||
|
||||
let js_blocks = JsArray::new(&mut cx, blocks.len() as u32);
|
||||
for (i, block) in blocks.iter().enumerate() {
|
||||
let inner = JsArray::new(&mut cx, block.len() as u32);
|
||||
for (j, uid) in block.iter().enumerate() {
|
||||
let v = cx.number(*uid);
|
||||
inner.set(&mut cx, j as u32, v)?;
|
||||
}
|
||||
js_blocks.set(&mut cx, i as u32, inner)?;
|
||||
}
|
||||
|
||||
let js_clusters = JsArray::new(&mut cx, clusters.len() as u32);
|
||||
for (i, cluster) in clusters.iter().enumerate() {
|
||||
let inner = JsArray::new(&mut cx, cluster.len() as u32);
|
||||
for (j, uid) in cluster.iter().enumerate() {
|
||||
let v = cx.number(*uid);
|
||||
inner.set(&mut cx, j as u32, v)?;
|
||||
}
|
||||
js_clusters.set(&mut cx, i as u32, inner)?;
|
||||
}
|
||||
|
||||
let js_rates = JsArray::new(&mut cx, rates.len() as u32);
|
||||
for (i, (uid, rate)) in rates.iter().enumerate() {
|
||||
let inner = JsArray::new(&mut cx, 2);
|
||||
let js_uid = cx.number(*uid);
|
||||
let js_rate = cx.number(*rate);
|
||||
inner.set(&mut cx, 0, js_uid)?;
|
||||
inner.set(&mut cx, 1, js_rate)?;
|
||||
js_rates.set(&mut cx, i as u32, inner)?;
|
||||
}
|
||||
|
||||
result.set(&mut cx, "blocks", js_blocks)?;
|
||||
result.set(&mut cx, "clusters", js_clusters)?;
|
||||
result.set(&mut cx, "rates", js_rates)?;
|
||||
|
||||
let callback = callback.into_inner(&mut cx);
|
||||
let this = cx.undefined();
|
||||
let args = vec![result.upcast()];
|
||||
|
||||
callback.call(&mut cx, this, args)?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
fn run_in_thread<F>(callback: F) -> Result<()>
|
||||
where
|
||||
F: Fn(GbtResult) -> Result<()>,
|
||||
{
|
||||
let handle = std::thread::spawn(|| -> Result<GbtResult> {
|
||||
let mut map = THREAD_TRANSACTIONS
|
||||
.lock()
|
||||
.map_err(|_| napi::Error::from_reason("THREAD_TRANSACTIONS Mutex poisoned"))?;
|
||||
gbt::gbt(&mut map).ok_or_else(|| napi::Error::from_reason("gbt failed"))
|
||||
});
|
||||
}
|
||||
|
||||
#[neon::main]
|
||||
fn main(mut cx: ModuleContext) -> NeonResult<()> {
|
||||
cx.export_function("make", make)?;
|
||||
cx.export_function("update", update)?;
|
||||
Ok(())
|
||||
callback(
|
||||
handle
|
||||
.join()
|
||||
.map_err(|_| napi::Error::from_reason("thread panicked"))??,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user