diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index f9d66fe7ffb5c889d04bf25c927b4b8458207c43..423c07f66d1b32a26a78aa1036fa454de7801f8a 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -1183,6 +1183,7 @@ export class FileDAL implements ServerDAO { } async removePeerByPubkey(pubkey: string) { + this.rustServer.removePeerByPubkey(pubkey); return this.peerDAL.removePeerByPubkey(pubkey); } @@ -1473,6 +1474,7 @@ export class FileDAL implements ServerDAO { } savePeer(peer: DBPeer) { + this.rustServer.savePeer(PeerDTO.fromDBPeer(peer)); return this.peerDAL.savePeer(peer); } @@ -1602,6 +1604,7 @@ export class FileDAL implements ServerDAO { } async resetPeers() { + this.rustServer.removeAllPeers(); await this.peerDAL.removeAll(); return await this.close(); } diff --git a/app/modules/ws2p/lib/WS2PCluster.ts b/app/modules/ws2p/lib/WS2PCluster.ts index bb49f3b0e933680d617dd2df681560dc3a4ac0b6..080150134257750cec254a0684776dca06c92cf0 100644 --- a/app/modules/ws2p/lib/WS2PCluster.ts +++ b/app/modules/ws2p/lib/WS2PCluster.ts @@ -25,7 +25,7 @@ import { PeerDTO, WS2PEndpoint } from "../../../lib/dto/PeerDTO"; import { GlobalFifoPromise } from "../../../service/GlobalFifoPromise"; import { OtherConstants } from "../../../lib/other_constants"; import { Key } from "../../../lib/common-libs/crypto/keyring"; -import { verify } from "../../../../neon/lib"; +import { RustServer, verify } from "../../../../neon/lib"; import { WS2PServerMessageHandler } from "./interface/WS2PServerMessageHandler"; import { WS2PMessageHandler } from "./impl/WS2PMessageHandler"; import { CommonConstants } from "../../../lib/common-libs/constants"; @@ -238,6 +238,9 @@ export class WS2PCluster { } }, WS2PConstants.HEADS_SPREAD_TIMEOUT); + // Send HEADs to rust server + this.server.dal.rustServer.receiveNewHeads(this.newHeads); + this.server.push({ ws2p: "heads", added: this.newHeads, diff --git a/neon/native/server.d.ts b/neon/native/server.d.ts index 39f626d27d773af9bea348fa4ee9856e6cc2c159..c65a605b1a53895010362d86abcc8a6aa0fe0838 100644 --- a/neon/native/server.d.ts +++ b/neon/native/server.d.ts @@ -2,12 +2,36 @@ import { TransactionDTOV10 } from './transaction'; -export class RustServerConf { - command: string | null - currency: string - gva: GvaConf | undefined - selfKeypair: string | null - txsMempoolSize: number +export class BlockDTOV10 { + version: number; + number: number; + currency: string; + hash: string; + inner_hash: string; + previousHash: string; + issuer: string; + previousIssuer: string; + dividend: number | null; + time: number; + powMin: number; + unitbase: number; + membersCount: number; + issuersCount: number; + issuersFrame: number; + issuersFrameVar: number; + identities: string[]; + joiners: string[]; + actives: string[]; + leavers: string[]; + revoked: string[]; + excluded: string[]; + certifications: string[]; + transactions: TransactionDTOV10[]; + medianTime: number; + nonce: number; + parameters: string | null; + signature: string; + monetaryMass: number; } export class GvaConf { @@ -24,6 +48,12 @@ export class GvaConf { whitelist?: string[]; } +export class HeadWS2Pv1 { + messageV2?: string; + sigV2?: string; + step?: number; +} + export class PeerCard { version: number currency: string @@ -51,6 +81,14 @@ export class RustDbTx { writtenTime: number; } +export class RustServerConf { + command: string | null + currency: string + gva: GvaConf | undefined + selfKeypair: string | null + txsMempoolSize: number +} + export class TxsHistory { sent: RustDbTx[]; received: RustDbTx[]; @@ -58,54 +96,35 @@ export class TxsHistory { pending: TransactionDTOV10[]; } -export class BlockDTOV10 { - version: number; - number: number; - currency: string; - hash: string; - inner_hash: string; - previousHash: string; - issuer: string; - previousIssuer: string; - dividend: number | null; - time: number; - powMin: number; - unitbase: number; - membersCount: number; - issuersCount: number; - issuersFrame: number; - issuersFrameVar: number; - identities: string[]; - joiners: string[]; - actives: string[]; - leavers: string[]; - revoked: string[]; - excluded: string[]; - certifications: string[]; - transactions: TransactionDTOV10[]; - medianTime: number; - nonce: number; - parameters: string | null; - signature: string; - monetaryMass: number; -} - export class RustServer { constructor(conf: RustServerConf, home: string | null); + // Indexing blockchain + revertBlock(block: BlockDTOV10): void; + applyBlock(block: BlockDTOV10): void; + applyChunkOfBlocks(blocks: BlockDTOV10[]): void; + + // Rust Endpoints (GVA, etc) + getSelfEndpoints(): string[]; + + // Txs mempool acceptNewTx(tx: TransactionDTOV10, serverPubkey: string): boolean; addPendingTx(tx: TransactionDTOV10): void; getMempoolTxsFreeRooms(): number; getNewPendingTxs(): TransactionDTOV10[]; - getSelfEndpoints(): string[]; - getTransactionsHistory(pubkey: string): TxsHistory; getTransactionsPending(versionMin: number, medianTime: number): TransactionDTOV10[]; - getTxByHash(hash: string): TransactionDTOV10 | null; removeAllPendingTxs(): void; removePendingTxByHash(hash: string): void; - revertBlock(block: BlockDTOV10): void; - applyBlock(block: BlockDTOV10): void; - applyChunkOfBlocks(blocks: BlockDTOV10[]): void; trimExpiredNonWrittenTxs(limitTime: number): void; + + // Transactions history (for BMA only) + getTransactionsHistory(pubkey: string): TxsHistory; + getTxByHash(hash: string): TransactionDTOV10 | null; + + // WS2Pv1: HEADs and peers + receiveNewHeads(heads: HeadWS2Pv1[]): void; + removeAllPeers(): void; + removePeerByPubkey(pubkey: string): void; + savePeer(peer: PeerCard): void; updateSelfPeer(peer: PeerCard): void; } diff --git a/neon/native/src/server.rs b/neon/native/src/server.rs index 769318dd08b48386215fef45bd5d9c95dddb867f..70a46f68d8ab7f64ce5414150ff6756e04a6cb20 100644 --- a/neon/native/src/server.rs +++ b/neon/native/src/server.rs @@ -82,22 +82,50 @@ declare_types! { }.map(|server| RustServer { server }) ) } - method acceptNewTx(mut cx) { - let tx_js = cx.argument::<JsValue>(0)?; - let server_pubkey_str = cx.argument::<JsString>(1)?.value(); - let tx_str: TransactionDocumentV10Stringified = neon_serde::from_value(&mut cx, tx_js)?; - let tx = into_neon_res(&mut cx, TransactionDocumentV10::from_string_object(&tx_str))?; - let server_pubkey = into_neon_res(&mut cx, PublicKey::from_base58(&server_pubkey_str))?; + // Indexing blockchain + method revertBlock(mut cx) { + let block_js = cx.argument::<JsValue>(0)?; - let this = cx.this(); + let block_stringified: dubp::block::DubpBlockV10Stringified = neon_serde::from_value(&mut cx, block_js)?; + + let mut this = cx.this(); let res = { let guard = cx.lock(); - let server = this.borrow(&guard); - server.server.accept_new_tx(tx, server_pubkey) - }.map(|accepted| cx.boolean(accepted).upcast()); + let mut server = this.borrow_mut(&guard); + server.server.revert_block(block_stringified) + }.map(|()| cx.undefined().upcast()); + into_neon_res(&mut cx, res) + } + method applyBlock(mut cx) { + let block_js = cx.argument::<JsValue>(0)?; + + let block_stringified: dubp::block::DubpBlockV10Stringified = neon_serde::from_value(&mut cx, block_js)?; + + let mut this = cx.this(); + let res = { + let guard = cx.lock(); + let mut server = this.borrow_mut(&guard); + server.server.apply_block(block_stringified) + }.map(|()| cx.undefined().upcast()); into_neon_res(&mut cx, res) } + method applyChunkOfBlocks(mut cx) { + let blocks_js = cx.argument::<JsValue>(0)?; + + let blocks_stringified: Vec<dubp::block::DubpBlockV10Stringified> = neon_serde::from_value(&mut cx, blocks_js)?; + + let mut this = cx.this(); + let res = { + let guard = cx.lock(); + let mut server = this.borrow_mut(&guard); + server.server.apply_chunk_of_blocks(blocks_stringified) + }.map(|()| cx.undefined().upcast()); + into_neon_res(&mut cx, res) + } + + + // Rust Endpoints (GVA, etc) method getSelfEndpoints(mut cx) { let this = cx.this(); let res = { @@ -114,54 +142,46 @@ declare_types! { }); into_neon_res(&mut cx, res) } - method getTxByHash(mut cx) { - let hash_str = cx.argument::<JsString>(0)?.value(); - let hash = into_neon_res(&mut cx, Hash::from_hex(&hash_str))?; + + + // Txs mempool + method acceptNewTx(mut cx) { + let tx_js = cx.argument::<JsValue>(0)?; + let server_pubkey_str = cx.argument::<JsString>(1)?.value(); + + let tx_str: TransactionDocumentV10Stringified = neon_serde::from_value(&mut cx, tx_js)?; + let tx = into_neon_res(&mut cx, TransactionDocumentV10::from_string_object(&tx_str))?; + let server_pubkey = into_neon_res(&mut cx, PublicKey::from_base58(&server_pubkey_str))?; let this = cx.this(); let res = { let guard = cx.lock(); let server = this.borrow(&guard); - server.server.get_tx_by_hash(hash) - }; - match res { - Ok(tx_opt) => if let Some((tx, written_block_opt)) = tx_opt { - let tx_js = neon_serde::to_value(&mut cx, &tx.to_string_object())?; - if let Some(written_block) = written_block_opt { - let written_block = cx.number(written_block.0); - let tx_js = tx_js.downcast_or_throw::<JsObject, _>(&mut cx)?; - tx_js.set(&mut cx, "writtenBlock", written_block)?; - } - Ok(tx_js.upcast()) - } else { - Ok(cx.null().upcast()) - }, - Err(e) => cx.throw_error(format!("{}", e)), - } + server.server.accept_new_tx(tx, server_pubkey) + }.map(|accepted| cx.boolean(accepted).upcast()); + into_neon_res(&mut cx, res) } - method removePendingTxByHash(mut cx) { - let hash_str = cx.argument::<JsString>(0)?.value(); - let hash = into_neon_res(&mut cx, Hash::from_hex(&hash_str))?; + method addPendingTx(mut cx) { + let tx_js = cx.argument::<JsValue>(0)?; + + let tx_str: TransactionDocumentV10Stringified = neon_serde::from_value(&mut cx, tx_js)?; + let tx = into_neon_res(&mut cx, TransactionDocumentV10::from_string_object(&tx_str))?; let this = cx.this(); let res = { let guard = cx.lock(); let server = this.borrow(&guard); - server.server.remove_pending_tx_by_hash(hash) - }.map(|()| cx.undefined().upcast()); + server.server.add_pending_tx_force(tx) + }.map(|_| cx.undefined().upcast()); into_neon_res(&mut cx, res) } - method revertBlock(mut cx) { - let block_js = cx.argument::<JsValue>(0)?; - - let block_stringified: dubp::block::DubpBlockV10Stringified = neon_serde::from_value(&mut cx, block_js)?; - - let mut this = cx.this(); + method getMempoolTxsFreeRooms(mut cx) { + let this = cx.this(); let res = { let guard = cx.lock(); - let mut server = this.borrow_mut(&guard); - server.server.revert_block(block_stringified) - }.map(|()| cx.undefined().upcast()); + let server = this.borrow(&guard); + server.server.get_mempool_txs_free_rooms() + }.map(|free_rooms| cx.number(free_rooms as f64).upcast()); into_neon_res(&mut cx, res) } method getNewPendingTxs(mut cx) { @@ -197,57 +217,40 @@ declare_types! { Err(e) => cx.throw_error(format!("{}", e)), } } - method trimExpiredNonWrittenTxs(mut cx) { - let limit_time = cx.argument::<JsNumber>(0)?.value() as i64; - + method removeAllPendingTxs(mut cx) { let this = cx.this(); let res = { let guard = cx.lock(); let server = this.borrow(&guard); - server.server.trim_expired_non_written_txs(limit_time) - }.map(|()| cx.undefined().upcast()); - into_neon_res(&mut cx, res) - } - method applyBlock(mut cx) { - let block_js = cx.argument::<JsValue>(0)?; - - let block_stringified: dubp::block::DubpBlockV10Stringified = neon_serde::from_value(&mut cx, block_js)?; - - let mut this = cx.this(); - let res = { - let guard = cx.lock(); - let mut server = this.borrow_mut(&guard); - server.server.apply_block(block_stringified) + server.server.remove_all_pending_txs() }.map(|()| cx.undefined().upcast()); into_neon_res(&mut cx, res) } - method applyChunkOfBlocks(mut cx) { - let blocks_js = cx.argument::<JsValue>(0)?; - - let blocks_stringified: Vec<dubp::block::DubpBlockV10Stringified> = neon_serde::from_value(&mut cx, blocks_js)?; + method removePendingTxByHash(mut cx) { + let hash_str = cx.argument::<JsString>(0)?.value(); + let hash = into_neon_res(&mut cx, Hash::from_hex(&hash_str))?; - let mut this = cx.this(); + let this = cx.this(); let res = { let guard = cx.lock(); - let mut server = this.borrow_mut(&guard); - server.server.apply_chunk_of_blocks(blocks_stringified) + let server = this.borrow(&guard); + server.server.remove_pending_tx_by_hash(hash) }.map(|()| cx.undefined().upcast()); into_neon_res(&mut cx, res) } - method addPendingTx(mut cx) { - let tx_js = cx.argument::<JsValue>(0)?; - - let tx_str: TransactionDocumentV10Stringified = neon_serde::from_value(&mut cx, tx_js)?; - let tx = into_neon_res(&mut cx, TransactionDocumentV10::from_string_object(&tx_str))?; + method trimExpiredNonWrittenTxs(mut cx) { + let limit_time = cx.argument::<JsNumber>(0)?.value() as i64; let this = cx.this(); let res = { let guard = cx.lock(); let server = this.borrow(&guard); - server.server.add_pending_tx_force(tx) - }.map(|_| cx.undefined().upcast()); + server.server.trim_expired_non_written_txs(limit_time) + }.map(|()| cx.undefined().upcast()); into_neon_res(&mut cx, res) } + + // Transactions history (for BMA only) method getTransactionsHistory(mut cx) { let pubkey_str = cx.argument::<JsString>(0)?.value(); let pubkey = into_neon_res(&mut cx, PublicKey::from_base58(&pubkey_str))?; @@ -281,21 +284,82 @@ declare_types! { Err(e) => cx.throw_error(format!("{}", e)), } } - method getMempoolTxsFreeRooms(mut cx) { + method getTxByHash(mut cx) { + let hash_str = cx.argument::<JsString>(0)?.value(); + let hash = into_neon_res(&mut cx, Hash::from_hex(&hash_str))?; + let this = cx.this(); let res = { let guard = cx.lock(); let server = this.borrow(&guard); - server.server.get_mempool_txs_free_rooms() - }.map(|free_rooms| cx.number(free_rooms as f64).upcast()); + server.server.get_tx_by_hash(hash) + }; + match res { + Ok(tx_opt) => if let Some((tx, written_block_opt)) = tx_opt { + let tx_js = neon_serde::to_value(&mut cx, &tx.to_string_object())?; + if let Some(written_block) = written_block_opt { + let written_block = cx.number(written_block.0); + let tx_js = tx_js.downcast_or_throw::<JsObject, _>(&mut cx)?; + tx_js.set(&mut cx, "writtenBlock", written_block)?; + } + Ok(tx_js.upcast()) + } else { + Ok(cx.null().upcast()) + }, + Err(e) => cx.throw_error(format!("{}", e)), + } + } + + // WS2Pv1: HEADs and peers + method receiveNewHeads(mut cx) { + let heads_js = cx.argument::<JsValue>(0)?; + + let heads_stringified: Vec<HeadWS2Pv1ConfStringified> = neon_serde::from_value(&mut cx, heads_js)?; + + use duniter_server::{DunpNodeIdV1Db, DunpHeadDbV1, KvResult}; + let heads = into_neon_res(&mut cx, heads_stringified.into_iter().map(|HeadWS2Pv1ConfStringified { message_v2, sig_v2, .. }| { + DunpHeadDbV1::from_stringified(&message_v2.unwrap_or_default(), &sig_v2.unwrap_or_default()) + }).collect::<KvResult<Vec<(DunpNodeIdV1Db, DunpHeadDbV1)>>>())?; + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let server = this.borrow(&guard); + server.server.receive_new_heads(heads) + }.map(|()| cx.undefined().upcast()); into_neon_res(&mut cx, res) } - method removeAllPendingTxs(mut cx) { + method removeAllPeers(mut cx) { let this = cx.this(); let res = { let guard = cx.lock(); let server = this.borrow(&guard); - server.server.remove_all_pending_txs() + server.server.remove_all_peers() + }.map(|()| cx.undefined().upcast()); + into_neon_res(&mut cx, res) + } + method removePeerByPubkey(mut cx) { + let pubkey_str = cx.argument::<JsString>(0)?.value(); + let pubkey = into_neon_res(&mut cx, PublicKey::from_base58(&pubkey_str))?; + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let server = this.borrow(&guard); + server.server.remove_peer_by_pubkey(pubkey) + }.map(|()| cx.undefined().upcast()); + into_neon_res(&mut cx, res) + } + method savePeer(mut cx) { + let peer_js = cx.argument::<JsValue>(0)?; + + let peer_stringified: PeerCardStringified = neon_serde::from_value(&mut cx, peer_js)?; + + let this = cx.this(); + let res = { + let guard = cx.lock(); + let server = this.borrow(&guard); + server.server.save_peer(peer_stringified) }.map(|()| cx.undefined().upcast()); into_neon_res(&mut cx, res) } @@ -315,24 +379,6 @@ declare_types! { } } -#[derive(Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] -struct RustServerConfStringified { - command_name: Option<String>, - currency: String, - gva: Option<GvaConf>, - self_keypair: Option<String>, - txs_mempool_size: u32, -} - -#[derive(Deserialize, Serialize)] -struct TxsHistoryStringified { - sent: Vec<DbTx>, - received: Vec<DbTx>, - sending: Vec<TransactionDocumentV10Stringified>, - pending: Vec<TransactionDocumentV10Stringified>, -} - #[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct DbTx { @@ -375,3 +421,29 @@ impl DbTx { } } } + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct HeadWS2Pv1ConfStringified { + message_v2: Option<String>, + sig_v2: Option<String>, + step: Option<usize>, +} + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct RustServerConfStringified { + command_name: Option<String>, + currency: String, + gva: Option<GvaConf>, + self_keypair: Option<String>, + txs_mempool_size: u32, +} + +#[derive(Deserialize, Serialize)] +struct TxsHistoryStringified { + sent: Vec<DbTx>, + received: Vec<DbTx>, + sending: Vec<TransactionDocumentV10Stringified>, + pending: Vec<TransactionDocumentV10Stringified>, +} diff --git a/rust-libs/duniter-dbs/src/databases.rs b/rust-libs/duniter-dbs/src/databases.rs index e459678400aa7193b167a1e585f2fb30cabe973d..1c49eafdf9c13bc331d7608754d4a7207a92bd9e 100644 --- a/rust-libs/duniter-dbs/src/databases.rs +++ b/rust-libs/duniter-dbs/src/databases.rs @@ -16,5 +16,6 @@ pub mod bc_v1; pub mod bc_v2; pub mod cm_v1; +pub mod dunp_v1; pub mod gva_v1; pub mod txs_mp_v2; diff --git a/rust-libs/duniter-dbs/src/databases/cm_v1.rs b/rust-libs/duniter-dbs/src/databases/cm_v1.rs index f94dc21764c4be1fb9d3a0060f6737dd17f28e61..9771aff3166742ec579561077204daf714a228ad 100644 --- a/rust-libs/duniter-dbs/src/databases/cm_v1.rs +++ b/rust-libs/duniter-dbs/src/databases/cm_v1.rs @@ -18,7 +18,7 @@ use crate::*; db_schema!( CmV1, [ - //["self_pubkey", self_pubkey, (), PubKeyValV2,], ["self_peer_old", SelfPeerOld, (), PeerCardDbV1], + ["dunp_heads_old", DunpHeadsOld, DunpNodeIdV1Db, DunpHeadDbV1], ] ); diff --git a/rust-libs/duniter-dbs/src/databases/dunp_v1.rs b/rust-libs/duniter-dbs/src/databases/dunp_v1.rs new file mode 100644 index 0000000000000000000000000000000000000000..86e4b320a356eb5c08979cbda2c3b5f4e80383d5 --- /dev/null +++ b/rust-libs/duniter-dbs/src/databases/dunp_v1.rs @@ -0,0 +1,21 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use crate::*; + +db_schema!( + DunpV1, + [["peers_old", PeersOld, PubKeyKeyV2, PeerCardDbV1],] +); diff --git a/rust-libs/duniter-dbs/src/keys.rs b/rust-libs/duniter-dbs/src/keys.rs index 70afba42bffbfa157e84cf7f9ba1779a1b204863..b5d2db16bd1451130a4bca39686bba24cfd933a1 100644 --- a/rust-libs/duniter-dbs/src/keys.rs +++ b/rust-libs/duniter-dbs/src/keys.rs @@ -16,6 +16,7 @@ pub mod all; pub mod block_number; pub mod blockstamp; +pub mod dunp_node_id; pub mod hash; pub mod pubkey; pub mod pubkey_and_sig; diff --git a/rust-libs/duniter-dbs/src/keys/dunp_node_id.rs b/rust-libs/duniter-dbs/src/keys/dunp_node_id.rs new file mode 100644 index 0000000000000000000000000000000000000000..e1edbb9693e4b44f95d544990c0116aaf8b4c34c --- /dev/null +++ b/rust-libs/duniter-dbs/src/keys/dunp_node_id.rs @@ -0,0 +1,95 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use crate::*; +use std::fmt::Display; +use uninit::prelude::*; + +#[derive( + Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, zerocopy::AsBytes, zerocopy::FromBytes, +)] +#[repr(transparent)] +pub struct DunpNodeIdV1Db([u8; 37]); // uuid ++ pubkey + +impl DunpNodeIdV1Db { + pub fn new(uuid: u32, pubkey: PublicKey) -> Self { + let mut buffer = uninit_array![u8; 37]; + let (uuid_buffer, pubkey_buffer) = buffer.as_out().split_at_out(4); + + uuid_buffer.copy_from_slice(&uuid.to_be_bytes()[..]); + pubkey_buffer.copy_from_slice(pubkey.as_ref()); + + Self(unsafe { std::mem::transmute(buffer) }) + } + pub fn get_uuid(&self) -> u32 { + let mut buffer = uninit_array![u8; 4]; + + buffer.as_out().copy_from_slice(&self.0[..4]); + + u32::from_be_bytes(unsafe { std::mem::transmute(buffer) }) + } + pub fn get_pubkey(&self) -> PublicKey { + let mut buffer = uninit_array![u8; 33]; + + buffer.as_out().copy_from_slice(&self.0[4..]); + let bytes: [u8; 33] = unsafe { std::mem::transmute(buffer) }; + + PublicKey::try_from(&bytes[..]).unwrap_or_else(|_| unreachable!()) + } +} + +impl Default for DunpNodeIdV1Db { + fn default() -> Self { + DunpNodeIdV1Db([0u8; 37]) + } +} + +impl Display for DunpNodeIdV1Db { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:x}-{}", self.get_uuid(), self.get_pubkey()) + } +} + +impl KeyAsBytes for DunpNodeIdV1Db { + fn as_bytes<T, F: FnMut(&[u8]) -> T>(&self, mut f: F) -> T { + f(self.0.as_ref()) + } +} + +impl kv_typed::prelude::FromBytes for DunpNodeIdV1Db { + type Err = StringErr; + + fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { + let layout = zerocopy::LayoutVerified::<_, DunpNodeIdV1Db>::new(bytes) + .ok_or_else(|| StringErr("corrupted db".to_owned()))?; + Ok(*layout) + } +} + +impl ToDumpString for DunpNodeIdV1Db { + fn to_dump_string(&self) -> String { + todo!() + } +} + +#[cfg(feature = "explorer")] +impl ExplorableKey for DunpNodeIdV1Db { + fn from_explorer_str(_: &str) -> std::result::Result<Self, StringErr> { + unimplemented!() + } + fn to_explorer_string(&self) -> KvResult<String> { + Ok(self.to_string()) + } +} diff --git a/rust-libs/duniter-dbs/src/lib.rs b/rust-libs/duniter-dbs/src/lib.rs index f96cd188544d5b6de98baf60a4d89ef040443808..a9e2d3dd487f8f620408dc23b15a7c8522987a36 100644 --- a/rust-libs/duniter-dbs/src/lib.rs +++ b/rust-libs/duniter-dbs/src/lib.rs @@ -55,6 +55,7 @@ pub use crate::keys::utxo_id::UtxoIdDbV2; pub use keys::all::AllKeyV1; pub use keys::block_number::BlockNumberKeyV1; pub use keys::blockstamp::BlockstampKeyV1; +pub use keys::dunp_node_id::DunpNodeIdV1Db; pub use keys::hash::{HashKeyV1, HashKeyV2}; pub use keys::pubkey::{PubKeyKeyV1, PubKeyKeyV2}; pub use keys::pubkey_and_sig::PubKeyAndSigV1; @@ -69,6 +70,7 @@ pub use values::block_head_db::BlockHeadDbV1; pub use values::block_meta::BlockMetaV2; pub use values::block_number_array_db::BlockNumberArrayV1; pub use values::cindex_db::CIndexDbV1; +pub use values::dunp_head::DunpHeadDbV1; pub use values::gva_idty_db::GvaIdtyDbV1; pub use values::idty_db::IdtyDbV2; pub use values::iindex_db::IIndexDbV1; @@ -115,7 +117,8 @@ pub type FileBackend = kv_typed::backend::memory::Mem; #[derive(Clone, Debug)] pub struct DuniterDbs<B: Backend> { pub bc_db_ro: databases::bc_v2::BcV2DbRo<B>, - pub cm_db: databases::cm_v1::CmV1Db<MemSingleton>, + pub cm_db: databases::cm_v1::CmV1Db<Mem>, + pub dunp_db: databases::dunp_v1::DunpV1Db<B>, pub gva_db: databases::gva_v1::GvaV1Db<B>, pub txs_mp_db: databases::txs_mp_v2::TxsMpV2Db<B>, } @@ -124,11 +127,13 @@ impl DuniterDbs<Mem> { pub fn mem() -> KvResult<Self> { use databases::bc_v2::BcV2DbWritable as _; use databases::cm_v1::CmV1DbWritable as _; + use databases::dunp_v1::DunpV1DbWritable as _; use databases::gva_v1::GvaV1DbWritable as _; use databases::txs_mp_v2::TxsMpV2DbWritable as _; Ok(DuniterDbs { bc_db_ro: databases::bc_v2::BcV2Db::<Mem>::open(MemConf::default())?.get_ro_handler(), - cm_db: databases::cm_v1::CmV1Db::<MemSingleton>::open(MemSingletonConf::default())?, + cm_db: databases::cm_v1::CmV1Db::<Mem>::open(MemConf::default())?, + dunp_db: databases::dunp_v1::DunpV1Db::<Mem>::open(MemConf::default())?, gva_db: databases::gva_v1::GvaV1Db::<Mem>::open(MemConf::default())?, txs_mp_db: databases::txs_mp_v2::TxsMpV2Db::<Mem>::open(MemConf::default())?, }) diff --git a/rust-libs/duniter-dbs/src/open_dbs.rs b/rust-libs/duniter-dbs/src/open_dbs.rs index 0c4795e506fff8971eec53f7dcf6aec0a71d7ba1..014d5aaf6f3f541626677c4944ddfc2e9b4161ae 100644 --- a/rust-libs/duniter-dbs/src/open_dbs.rs +++ b/rust-libs/duniter-dbs/src/open_dbs.rs @@ -15,6 +15,7 @@ use crate::databases::bc_v2::BcV2DbWritable as _; use crate::databases::cm_v1::CmV1DbWritable as _; +use crate::databases::dunp_v1::DunpV1DbWritable as _; use crate::databases::gva_v1::GvaV1DbWritable as _; use crate::databases::txs_mp_v2::TxsMpV2DbWritable as _; use crate::*; @@ -27,8 +28,13 @@ pub fn open_dbs<B: BackendConf>( .expect("fail to open BcV2 DB"); let dbs = DuniterDbs { bc_db_ro: bc_db.get_ro_handler(), - cm_db: crate::databases::cm_v1::CmV1Db::<MemSingleton>::open(MemSingletonConf::default()) + cm_db: crate::databases::cm_v1::CmV1Db::<Mem>::open(MemConf::default()) .expect("fail to open CmV1 DB"), + dunp_db: crate::databases::dunp_v1::DunpV1Db::<B>::open(B::gen_backend_conf( + "dunp_v1", + home_path_opt, + )) + .expect("fail to open Dunp DB"), gva_db: crate::databases::gva_v1::GvaV1Db::<B>::open(B::gen_backend_conf( "gva_v1", home_path_opt, diff --git a/rust-libs/duniter-dbs/src/values.rs b/rust-libs/duniter-dbs/src/values.rs index a0bc4209b094c8d24fb412447c07142e07258f82..27a5ce4fa70dc8404ee320022e08bcb26ba191bc 100644 --- a/rust-libs/duniter-dbs/src/values.rs +++ b/rust-libs/duniter-dbs/src/values.rs @@ -18,6 +18,7 @@ pub mod block_head_db; pub mod block_meta; pub mod block_number_array_db; pub mod cindex_db; +pub mod dunp_head; pub mod gva_idty_db; pub mod idty_db; pub mod iindex_db; diff --git a/rust-libs/duniter-dbs/src/values/dunp_head.rs b/rust-libs/duniter-dbs/src/values/dunp_head.rs new file mode 100644 index 0000000000000000000000000000000000000000..2ab352e9b61bdcffba1be8160b0733b0c667b63e --- /dev/null +++ b/rust-libs/duniter-dbs/src/values/dunp_head.rs @@ -0,0 +1,127 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use crate::*; + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] +pub struct DunpHeadDbV1 { + pub api: String, + pub pubkey: PublicKey, + pub blockstamp: Blockstamp, + pub software: String, + pub software_version: String, + pub pow_prefix: u32, + pub free_member_room: u32, + pub free_mirror_room: u32, + pub signature: Signature, +} + +impl DunpHeadDbV1 { + pub fn from_stringified(message_v2: &str, signature: &str) -> KvResult<(DunpNodeIdV1Db, Self)> { + let signature = + Signature::from_base64(signature).map_err(|e| KvError::DeserError(e.to_string()))?; + + let strs: SmallVec<[&str; 11]> = message_v2.split(':').collect(); + if strs.len() < 11 { + return Err(KvError::DeserError( + "DunpHeadDbV1::from_stringified(): invalid message_v2".to_owned(), + )); + } + + let uuid = + u32::from_str_radix(strs[5], 16).map_err(|e| KvError::DeserError(e.to_string()))?; + let pubkey = + PublicKey::from_base58(strs[3]).map_err(|e| KvError::DeserError(e.to_string()))?; + let blockstamp = + Blockstamp::from_str(strs[4]).map_err(|e| KvError::DeserError(e.to_string()))?; + + Ok(( + DunpNodeIdV1Db::new(uuid, pubkey), + DunpHeadDbV1 { + api: strs[0].to_owned(), + pubkey, + blockstamp, + software: strs[6].to_owned(), + software_version: strs[7].to_owned(), + pow_prefix: u32::from_str(strs[8]) + .map_err(|e| KvError::DeserError(e.to_string()))?, + free_member_room: u32::from_str(strs[9]) + .map_err(|e| KvError::DeserError(e.to_string()))?, + free_mirror_room: u32::from_str(strs[10]) + .map_err(|e| KvError::DeserError(e.to_string()))?, + signature, + }, + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_dunp_head_from_stringified() -> KvResult<()> { + let message = "WS2POCAIC:HEAD:2:GX1nYVburxeaVP1SCNuhVKwNy6M2h6wPamHhyoSF4Ccn:379783-0000001BB2B88D077605C1330CA60AA222624FAA3BA60566D6CA51A9122376F7:882a5ad1:duniter:1.8.1:1:1:1"; + let sig = "qBvJ7JZ4i8tKeItmZ/lurzr5O2/jKnB1reoIjEIl5x6sqbAhVsVsHut85yQoP30tURGfVX5FwMhCuC4DvCSFCg=="; + let (node_id, head) = DunpHeadDbV1::from_stringified(message, sig)?; + + assert_eq!(&format!("{:x}", node_id.get_uuid()), "882a5ad1"); + assert_eq!( + &node_id.get_pubkey().to_string(), + "GX1nYVburxeaVP1SCNuhVKwNy6M2h6wPamHhyoSF4Ccn" + ); + assert_eq!(&head.api, "WS2POCAIC"); + assert_eq!( + &head.pubkey.to_string(), + "GX1nYVburxeaVP1SCNuhVKwNy6M2h6wPamHhyoSF4Ccn" + ); + assert_eq!( + &head.blockstamp.to_string(), + "379783-0000001BB2B88D077605C1330CA60AA222624FAA3BA60566D6CA51A9122376F7" + ); + Ok(()) + } +} + +impl ValueAsBytes for DunpHeadDbV1 { + fn as_bytes<T, F: FnMut(&[u8]) -> KvResult<T>>(&self, mut f: F) -> KvResult<T> { + let bytes = bincode::serialize(self).map_err(|e| KvError::DeserError(format!("{}", e)))?; + f(bytes.as_ref()) + } +} + +impl kv_typed::prelude::FromBytes for DunpHeadDbV1 { + type Err = StringErr; + + fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Err> { + Ok(bincode::deserialize(&bytes).map_err(|e| StringErr(format!("{}: '{:?}'", e, bytes)))?) + } +} + +impl ToDumpString for DunpHeadDbV1 { + fn to_dump_string(&self) -> String { + todo!() + } +} + +#[cfg(feature = "explorer")] +impl ExplorableValue for DunpHeadDbV1 { + fn from_explorer_str(_source: &str) -> std::result::Result<Self, StringErr> { + unimplemented!() + } + fn to_explorer_json(&self) -> KvResult<serde_json::Value> { + serde_json::to_value(self).map_err(|e| KvError::DeserError(format!("{}", e))) + } +} diff --git a/rust-libs/duniter-server/src/lib.rs b/rust-libs/duniter-server/src/lib.rs index 8b340dac3a3071ab0f5beffc71ca91493c0b4368..ac936bfedd8bdc46c702e835e9323baa2ff96362 100644 --- a/rust-libs/duniter-server/src/lib.rs +++ b/rust-libs/duniter-server/src/lib.rs @@ -23,7 +23,7 @@ )] pub use duniter_conf::DuniterConf; -pub use duniter_dbs::smallvec; +pub use duniter_dbs::{kv_typed::prelude::KvResult, smallvec, DunpHeadDbV1, DunpNodeIdV1Db}; pub use duniter_gva::{GvaConf, GvaModule, PeerCardStringified}; use anyhow::Context; @@ -41,7 +41,7 @@ use duniter_dbs::{ txs_mp_v2::TxsMpV2DbReadable, }, kv_typed::prelude::*, - HashKeyV2, PendingTxDbV2, + HashKeyV2, PendingTxDbV2, PubKeyKeyV2, }; use duniter_dbs::{prelude::*, BlockMetaV2, FileBackend}; use duniter_gva_dbs_reader::txs_history::TxsHistory; @@ -250,7 +250,52 @@ impl DuniterServer { .execute(move |dbs| txs_mempool.add_pending_tx_force(&dbs.txs_mp_db, &tx)) .expect("dbs pool disconnected") } - + pub fn apply_block(&mut self, block: DubpBlockV10Stringified) -> KvResult<()> { + let block = Arc::new( + DubpBlockV10::from_string_object(&block) + .map_err(|e| KvError::DeserError(format!("{}", e)))?, + ); + self.current = Some(duniter_dbs_write_ops::apply_block::apply_block( + &self.bc_db, + block.clone(), + self.current, + &self.dbs_pool, + false, + )?); + apply_block_modules(block, &self.conf, &self.dbs_pool, None) + } + pub fn apply_chunk_of_blocks(&mut self, blocks: Vec<DubpBlockV10Stringified>) -> KvResult<()> { + log::debug!("apply_chunk(#{})", blocks[0].number); + let blocks = Arc::from( + blocks + .into_iter() + .map(|block| DubpBlockV10::from_string_object(&block)) + .collect::<Result<Vec<_>, _>>() + .map_err(|e| KvError::DeserError(format!("{}", e)))?, + ); + self.current = Some(duniter_dbs_write_ops::apply_block::apply_chunk( + &self.bc_db, + self.current, + &self.dbs_pool, + blocks.clone(), + )?); + apply_chunk_of_blocks_modules(blocks, &self.conf, &self.dbs_pool, None) + } + pub fn receive_new_heads( + &self, + heads: Vec<(duniter_dbs::DunpNodeIdV1Db, duniter_dbs::DunpHeadDbV1)>, + ) -> KvResult<()> { + self.dbs_pool + .execute(move |dbs| { + for (dunp_node_id, dunp_head) in heads { + dbs.cm_db + .dunp_heads_old_write() + .upsert(dunp_node_id, dunp_head)? + } + Ok::<(), KvError>(()) + }) + .expect("dbs pool disconnected") + } pub fn remove_all_pending_txs(&self) -> KvResult<()> { self.dbs_pool .execute(move |dbs| { @@ -284,36 +329,39 @@ impl DuniterServer { txs_mp_job_handle.join().expect("dbs pool disconnected")?; revert_block_modules(block, &self.conf, &self.dbs_pool, None) } - pub fn apply_block(&mut self, block: DubpBlockV10Stringified) -> KvResult<()> { - let block = Arc::new( - DubpBlockV10::from_string_object(&block) - .map_err(|e| KvError::DeserError(format!("{}", e)))?, - ); - self.current = Some(duniter_dbs_write_ops::apply_block::apply_block( - &self.bc_db, - block.clone(), - self.current, - &self.dbs_pool, - false, - )?); - apply_block_modules(block, &self.conf, &self.dbs_pool, None) + pub fn remove_all_peers(&self) -> KvResult<()> { + use duniter_dbs::databases::dunp_v1::DunpV1DbWritable as _; + self.dbs_pool + .execute(move |dbs| dbs.dunp_db.peers_old_write().clear()) + .expect("dbs pool disconnected") } - pub fn apply_chunk_of_blocks(&mut self, blocks: Vec<DubpBlockV10Stringified>) -> KvResult<()> { - log::debug!("apply_chunk(#{})", blocks[0].number); - let blocks = Arc::from( - blocks - .into_iter() - .map(|block| DubpBlockV10::from_string_object(&block)) - .collect::<Result<Vec<_>, _>>() - .map_err(|e| KvError::DeserError(format!("{}", e)))?, - ); - self.current = Some(duniter_dbs_write_ops::apply_block::apply_chunk( - &self.bc_db, - self.current, - &self.dbs_pool, - blocks.clone(), - )?); - apply_chunk_of_blocks_modules(blocks, &self.conf, &self.dbs_pool, None) + pub fn remove_peer_by_pubkey(&self, pubkey: PublicKey) -> KvResult<()> { + use duniter_dbs::databases::dunp_v1::DunpV1DbWritable as _; + self.dbs_pool + .execute(move |dbs| dbs.dunp_db.peers_old_write().remove(PubKeyKeyV2(pubkey))) + .expect("dbs pool disconnected") + } + pub fn save_peer(&self, new_peer_card: PeerCardStringified) -> anyhow::Result<()> { + use dubp::crypto::keys::PublicKey as _; + let pubkey = PublicKey::from_base58(&new_peer_card.pubkey)?; + use duniter_dbs::databases::dunp_v1::DunpV1DbWritable as _; + self.dbs_pool + .execute(move |dbs| { + dbs.dunp_db.peers_old_write().upsert( + PubKeyKeyV2(pubkey), + duniter_dbs::PeerCardDbV1 { + version: new_peer_card.version, + currency: new_peer_card.currency, + pubkey: new_peer_card.pubkey, + blockstamp: new_peer_card.blockstamp, + endpoints: new_peer_card.endpoints, + status: new_peer_card.status, + signature: new_peer_card.signature, + }, + ) + }) + .expect("dbs pool disconnected") + .map_err(|e| e.into()) } pub fn trim_expired_non_written_txs(&self, limit_time: i64) -> KvResult<()> { self.dbs_pool diff --git a/test/dal/basic-dal-tests.ts b/test/dal/basic-dal-tests.ts index 7891325bc433d04a5fcc0434a52d02a5ca1454f2..a3e39fffca82e792eeebec22d562066c874812d9 100644 --- a/test/dal/basic-dal-tests.ts +++ b/test/dal/basic-dal-tests.ts @@ -26,12 +26,14 @@ var constants = require('../../app/lib/constants'); var mocks = { peer1: { pubkey: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', - block: '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855', - currency: 'bb', - version: constants.DOCUMENTS_VERSION, - endpoints: [ + blockstamp: '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855', + currency: 'bb', + version: constants.DOCUMENTS_VERSION, + endpoints: [ 'BASIC_MERKLED_API localhost 7777' - ] + ], + status: "UP", + signature: "T84YUhY5PeOH1cmlxn+UCG0YjYQSnpKRlyHTlsDTLB19QneCAIxDrxx+Yz/VfzXyq3B5ScjpQG5MQ45wI+tZAw==", }, block0: { "issuersCount": 0,