diff --git a/Cargo.lock b/Cargo.lock index d8f1bd6ad..350e25fb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,27 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bitflags" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" + [[package]] name = "bytes" version = "1.4.0" @@ -20,12 +35,33 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1586fa608b1dab41f667475b4a41faec5ba680aee428bfa5de4ea520fdc6e901" +dependencies = [ + "quote", + "syn 2.0.20", +] + [[package]] name = "gbt" version = "0.1.0" dependencies = [ "bytes", - "neon", + "napi", + "napi-build", + "napi-derive", "once_cell", "priority-queue", ] @@ -48,53 +84,75 @@ dependencies = [ [[package]] name = "libloading" -version = "0.6.7" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", ] [[package]] -name = "neon" -version = "0.10.1" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e15415261d880aed48122e917a45e87bb82cf0260bb6db48bbab44b7464373" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "napi" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede2d12cd6fce44da537a4be1f5510c73be2506c2e32dfaaafd1f36968f3a0e" dependencies = [ - "neon-build", - "neon-macros", - "neon-runtime", - "semver", - "smallvec", + "bitflags", + "ctor", + "napi-derive", + "napi-sys", + "once_cell", ] [[package]] -name = "neon-build" -version = "0.10.1" +name = "napi-build" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bac98a702e71804af3dacfde41edde4a16076a7bbe889ae61e56e18c5b1c811" +checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e" [[package]] -name = "neon-macros" -version = "0.10.1" +name = "napi-derive" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7288eac8b54af7913c60e0eb0e2a7683020dffa342ab3fd15e28f035ba897cf" -dependencies = [ - "quote", - "syn", - "syn-mid", -] - -[[package]] -name = "neon-runtime" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4676720fa8bb32c64c3d9f49c47a47289239ec46b4bdb66d0913cc512cb0daca" +checksum = "da1c6a8fa84d549aa8708fcd062372bf8ec6e849de39016ab921067d21bde367" dependencies = [ "cfg-if", + "convert_case", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "napi-derive-backend" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20bbc7c69168d06a848f925ec5f0e0997f98e8c8d4f2cc30157f0da51c009e17" +dependencies = [ + "convert_case", + "once_cell", + "proc-macro2", + "quote", + "regex", + "semver", + "syn 1.0.109", +] + +[[package]] +name = "napi-sys" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "166b5ef52a3ab5575047a9fe8d4a030cdd0f63c96f071cd6907674453b07bae3" +dependencies = [ "libloading", - "smallvec", ] [[package]] @@ -132,25 +190,27 @@ dependencies = [ ] [[package]] -name = "semver" -version = "0.9.0" +name = "regex" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ - "semver-parser", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "semver-parser" -version = "0.7.0" +name = "regex-syntax" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" [[package]] -name = "smallvec" -version = "1.10.0" +name = "semver" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "syn" @@ -164,14 +224,14 @@ dependencies = [ ] [[package]] -name = "syn-mid" -version = "0.5.3" +name = "syn" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baa8e7560a164edb1621a55d18a0c59abf49d360f47aa7b821061dd7eea7fac9" +checksum = "fcb8d4cebc40aa517dfb69618fa647a346562e67228e2236ae0042ee6ac14775" dependencies = [ "proc-macro2", "quote", - "syn", + "unicode-ident", ] [[package]] @@ -180,6 +240,12 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index e682bbcbc..59562297c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,8 @@ [workspace] members = [ "./backend/rust-gbt", -] \ No newline at end of file +] + +[profile.release] +lto = true +codegen-units = 1 diff --git a/backend/rust-gbt/.gitignore b/backend/rust-gbt/.gitignore index 17ca706a8..6b0a536a0 100644 --- a/backend/rust-gbt/.gitignore +++ b/backend/rust-gbt/.gitignore @@ -1,4 +1,4 @@ -index.node +*.node **/node_modules **/.DS_Store npm-debug.log* diff --git a/backend/rust-gbt/Cargo.toml b/backend/rust-gbt/Cargo.toml index 9ff511966..bf5073240 100644 --- a/backend/rust-gbt/Cargo.toml +++ b/backend/rust-gbt/Cargo.toml @@ -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" diff --git a/backend/rust-gbt/build.rs b/backend/rust-gbt/build.rs new file mode 100644 index 000000000..0f1b01002 --- /dev/null +++ b/backend/rust-gbt/build.rs @@ -0,0 +1,3 @@ +fn main() { + napi_build::setup(); +} diff --git a/backend/rust-gbt/index.d.ts b/backend/rust-gbt/index.d.ts new file mode 100644 index 000000000..00d7f05a9 --- /dev/null +++ b/backend/rust-gbt/index.d.ts @@ -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> + clusters: Array> + rates: Array> + constructor(blocks: Array>, clusters: Array>, rates: Array>) +} diff --git a/backend/rust-gbt/index.js b/backend/rust-gbt/index.js new file mode 100644 index 000000000..5caf75b42 --- /dev/null +++ b/backend/rust-gbt/index.js @@ -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 diff --git a/backend/rust-gbt/package-lock.json b/backend/rust-gbt/package-lock.json index 6a6cc5fb7..5e23e425e 100644 --- a/backend/rust-gbt/package-lock.json +++ b/backend/rust-gbt/package-lock.json @@ -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" } } } diff --git a/backend/rust-gbt/package.json b/backend/rust-gbt/package.json index bb45af85e..3c6c7a8f5 100644 --- a/backend/rust-gbt/package.json +++ b/backend/rust-gbt/package.json @@ -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" } } \ No newline at end of file diff --git a/backend/rust-gbt/src/gbt.rs b/backend/rust-gbt/src/gbt.rs index 9cab724a4..b9dab0fd3 100644 --- a/backend/rust-gbt/src/gbt.rs +++ b/backend/rust-gbt/src/gbt.rs @@ -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, f64)>, Vec>); - /* * 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, f64)>, Vec>); pub fn gbt(mempool: &mut HashMap) -> Option { let mut audit_pool: HashMap = HashMap::new(); let mut mempool_array: VecDeque = VecDeque::new(); - let mut cluster_array: Vec> = Vec::new(); + let mut clusters: Vec> = Vec::new(); // Initialize working structs for (uid, tx) in mempool { @@ -130,7 +125,7 @@ pub fn gbt(mempool: &mut HashMap) -> Option { 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) -> Option { } // make a list of dirty transactions and their new rates - let mut rates: Vec<(u32, f64)> = Vec::new(); + let mut rates: Vec> = 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) { diff --git a/backend/rust-gbt/src/lib.rs b/backend/rust-gbt/src/lib.rs index 1bf06f780..e0c6ebde5 100644 --- a/backend/rust-gbt/src/lib.rs +++ b/backend/rust-gbt/src/lib.rs @@ -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>> = Lazy::new(|| Mutex::new(HashMap::new())); -fn make(mut cx: FunctionContext) -> JsResult { - let mempool_arg = cx - .argument::(0)? - .root(&mut cx) - .into_inner(&mut cx); - let callback = cx.argument::(1)?.root(&mut cx); - let channel = cx.channel(); - - let buffer = mempool_arg.as_slice(&cx); - +#[napi] +pub fn make(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 { - let new_txs_arg = cx - .argument::(0)? - .root(&mut cx) - .into_inner(&mut cx); - let remove_txs_arg = cx - .argument::(1)? - .root(&mut cx) - .into_inner(&mut cx); - let callback = cx.argument::(2)?.root(&mut cx); - let channel = cx.channel(); - +#[napi] +pub fn update(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) { - 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>, + pub clusters: Vec>, + pub rates: Vec>, // 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(callback: F) -> Result<()> +where + F: Fn(GbtResult) -> Result<()>, +{ + let handle = std::thread::spawn(|| -> Result { + 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"))??, + ) }