Skip to content

Commit 7b432b6

Browse files
committed
writer: establish wallet model
1 parent db5d5f9 commit 7b432b6

10 files changed

Lines changed: 550 additions & 42 deletions

File tree

Cargo.lock

Lines changed: 305 additions & 34 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ cyphernet = { version = "0.5.2", features = ["tor", "dns", "ed25519", "p2p-ed255
2929
microservices = { version = "0.12.0" }
3030
netservices = { version = "0.12.0-beta.1", features = ["eidolon", "reactor"] }
3131
crossbeam-channel = "0.5.15"
32+
native_db = "0.8.2"
33+
native_model = "0.4.20"
3234

3335
serde = { version = "1", features = ["derive"] }
3436
serde_yaml = "0.9.34"
@@ -77,6 +79,8 @@ io-reactor.workspace = true
7779
microservices = { workspace = true, features = ["log"] }
7880
netservices = { workspace = true, features = ["log"] }
7981
crossbeam-channel.workspace = true
82+
native_db.workspace = true
83+
native_model.workspace = true
8084
async-channel = { version = "2.3.1", optional = true }
8185
serde.workspace = true
8286
log.workspace = true

rpc/src/response.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use bpstd::psbt::Utxo;
2727
use bpstd::seals::TxoSeal;
2828
use netservices::Frame;
2929
use rgb::{ContractState, ContractStateName, WitnessStatus};
30-
use rgbp::descriptor::RgbDescr;
30+
use rgbp::descriptors::RgbDescr;
3131
use sonicapi::{CellAddr, CodexId, ContractId, Opid, StateAtom};
3232
use strict_types::StrictVal;
3333

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,20 @@ compile_error!("Either `embedded` or `server` feature must be used");
2727
#[macro_use]
2828
extern crate amplify;
2929
#[macro_use]
30+
extern crate native_db;
31+
#[macro_use]
32+
extern crate native_model;
33+
#[macro_use]
3034
extern crate serde;
3135

3236
mod config;
37+
mod model;
3338
pub mod services;
3439
mod workers;
3540
mod reactors;
3641

3742
pub use config::Config;
43+
pub use model::{DbHolder, DbUtxos, UtxoId, UtxoModel};
3844
pub use reactors::*;
3945
pub use workers::*;
4046

src/model/db.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// RGB Node: sovereign smart contracts backend
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Designed in 2020-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
6+
// Written in 2020-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7+
//
8+
// Copyright (C) 2020-2024 LNP/BP Standards Association. All rights reserved.
9+
// Copyright (C) 2025 RGB Consortium, Switzerland. All rights reserved.
10+
// Copyright (C) 2020-2025 Dr Maxim Orlovsky. All rights reserved.
11+
//
12+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
13+
// in compliance with the License. You may obtain a copy of the License at
14+
//
15+
// http://www.apache.org/licenses/LICENSE-2.0
16+
//
17+
// Unless required by applicable law or agreed to in writing, software distributed under the License
18+
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
19+
// or implied. See the License for the specific language governing permissions and limitations under
20+
// the License.
21+
22+
use std::collections::HashSet;
23+
use std::ops::{Deref, DerefMut};
24+
use std::sync::LazyLock;
25+
26+
use bpstd::psbt::Utxo;
27+
use bpstd::{Keychain, NormalIndex, Outpoint, Sats, Terminal, XpubDerivable};
28+
use native_db::{Builder, Database, Models, db_type};
29+
use rgbp::descriptors::RgbDescr;
30+
use rgbp::{MultiHolder, OwnerProvider, UtxoSet};
31+
32+
use crate::model::UtxoModel;
33+
34+
static MODELS: LazyLock<Models> = LazyLock::new(|| {
35+
let mut models = Models::new();
36+
models.define::<UtxoModel>().unwrap();
37+
models
38+
});
39+
40+
fn db() -> Result<Database<'static>, db_type::Error> { Builder::new().create_in_memory(&MODELS) }
41+
42+
pub struct DbHolder {
43+
inner: MultiHolder<XpubDerivable, DbUtxos>,
44+
db: Database<'static>,
45+
}
46+
47+
impl Deref for DbHolder {
48+
type Target = MultiHolder<XpubDerivable, DbUtxos>;
49+
50+
fn deref(&self) -> &Self::Target { &self.inner }
51+
}
52+
53+
impl DerefMut for DbHolder {
54+
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner }
55+
}
56+
57+
impl DbHolder {
58+
pub fn load() -> Result<Self, db_type::Error> {
59+
let db = db()?;
60+
let inner = MultiHolder::new();
61+
// TODO: populate with data
62+
Ok(Self { inner, db })
63+
}
64+
}
65+
66+
impl OwnerProvider for DbHolder {
67+
type Key = XpubDerivable;
68+
type UtxoSet = DbUtxos;
69+
70+
fn descriptor(&self) -> &RgbDescr<Self::Key> { self.inner.descriptor() }
71+
72+
fn utxos(&self) -> &Self::UtxoSet { self.inner.utxos() }
73+
74+
fn descriptor_mut(&mut self) -> &mut RgbDescr<Self::Key> { self.inner.descriptor_mut() }
75+
76+
fn utxos_mut(&mut self) -> &mut Self::UtxoSet { self.inner.utxos_mut() }
77+
}
78+
79+
pub struct DbUtxos {
80+
db: Database<'static>,
81+
}
82+
83+
impl DbUtxos {
84+
pub fn utxos(&self) -> HashSet<Utxo> { todo!() }
85+
}
86+
87+
impl UtxoSet for DbUtxos {
88+
fn len(&self) -> usize { todo!() }
89+
90+
fn has(&self, outpoint: Outpoint) -> bool { todo!() }
91+
92+
fn get(&self, outpoint: Outpoint) -> Option<(Sats, Terminal)> { todo!() }
93+
94+
fn insert(&mut self, outpoint: Outpoint, value: Sats, terminal: Terminal) { todo!() }
95+
96+
fn insert_all(&mut self, utxos: impl IntoIterator<Item = Utxo>) { todo!() }
97+
98+
fn clear(&mut self) { todo!() }
99+
100+
fn remove(&mut self, outpoint: Outpoint) -> Option<(Sats, Terminal)> { todo!() }
101+
102+
fn remove_all(&mut self, outpoints: impl IntoIterator<Item = Outpoint>) { todo!() }
103+
104+
fn outpoints(&self) -> impl Iterator<Item = Outpoint> {
105+
todo!();
106+
std::iter::empty()
107+
}
108+
109+
fn next_index_noshift(&self, keychain: impl Into<Keychain>) -> NormalIndex { todo!() }
110+
111+
fn next_index(&mut self, keychain: impl Into<Keychain>, shift: bool) -> NormalIndex { todo!() }
112+
}

src/model/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RGB Node: sovereign smart contracts backend
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Designed in 2020-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
6+
// Written in 2020-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7+
//
8+
// Copyright (C) 2020-2024 LNP/BP Standards Association. All rights reserved.
9+
// Copyright (C) 2025 RGB Consortium, Switzerland. All rights reserved.
10+
// Copyright (C) 2020-2025 Dr Maxim Orlovsky. All rights reserved.
11+
//
12+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
13+
// in compliance with the License. You may obtain a copy of the License at
14+
//
15+
// http://www.apache.org/licenses/LICENSE-2.0
16+
//
17+
// Unless required by applicable law or agreed to in writing, software distributed under the License
18+
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
19+
// or implied. See the License for the specific language governing permissions and limitations under
20+
// the License.
21+
22+
mod utxo;
23+
mod db;
24+
25+
pub use db::{DbHolder, DbUtxos};
26+
pub use utxo::{UtxoId, UtxoModel};

src/model/utxo.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RGB Node: sovereign smart contracts backend
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
// Designed in 2020-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
6+
// Written in 2020-2025 by Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7+
//
8+
// Copyright (C) 2020-2024 LNP/BP Standards Association. All rights reserved.
9+
// Copyright (C) 2025 RGB Consortium, Switzerland. All rights reserved.
10+
// Copyright (C) 2020-2025 Dr Maxim Orlovsky. All rights reserved.
11+
//
12+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
13+
// in compliance with the License. You may obtain a copy of the License at
14+
//
15+
// http://www.apache.org/licenses/LICENSE-2.0
16+
//
17+
// Unless required by applicable law or agreed to in writing, software distributed under the License
18+
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
19+
// or implied. See the License for the specific language governing permissions and limitations under
20+
// the License.
21+
22+
use bpstd::{DescrId, Outpoint, Sats, Terminal};
23+
use native_db::{Key, ToKey};
24+
use native_model::Model;
25+
26+
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
27+
#[derive(Serialize, Deserialize)]
28+
pub struct UtxoId {
29+
pub descr: DescrId,
30+
pub outpoint: Outpoint,
31+
}
32+
33+
impl ToKey for UtxoId {
34+
fn to_key(&self) -> Key { (self.descr.0, self.outpoint.to_string()).to_key() }
35+
36+
fn key_names() -> Vec<String> { vec!["utxo_id".to_string()] }
37+
}
38+
39+
#[derive(PartialEq, Debug)]
40+
#[derive(Serialize, Deserialize)]
41+
#[native_model(id = 1, version = 1)]
42+
#[native_db(primary_key(utxo_id -> UtxoId))]
43+
pub struct UtxoModel {
44+
descr: DescrId,
45+
outpoint: Outpoint,
46+
terminal: Terminal,
47+
value: Sats,
48+
}
49+
50+
impl UtxoModel {
51+
pub fn utxo_id(&self) -> UtxoId { UtxoId { descr: self.descr, outpoint: self.outpoint } }
52+
}

src/services/reader.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crossbeam_channel::Sender;
2929
use microservices::UService;
3030
use rgb::popls::bp::seals::TxoSeal;
3131
use rgb::{CellAddr, ContractId, ContractState, ContractStateName};
32-
use rgbp::descriptor::RgbDescr;
32+
use rgbp::descriptors::RgbDescr;
3333
use rgbrpc::WalletInfo;
3434
use strict_types::StrictVal;
3535

src/services/writer.rs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,21 @@
1919
// or implied. See the License for the specific language governing permissions and limitations under
2020
// the License.
2121

22+
use std::collections::HashSet;
2223
use std::convert::Infallible;
2324
use std::ops::ControlFlow;
2425

26+
use bpstd::psbt::PsbtConstructor;
2527
use bpstd::seals::TxoSeal;
28+
use bpstd::{Network, XpubDerivable};
2629
use microservices::{USender, UService};
2730
use rgb::{Contracts, Pile, Stockpile};
31+
use rgbp::resolvers::MultiResolver;
32+
use rgbp::{Owner, OwnerProvider, RgbRuntime};
2833

2934
use super::Request2Reader;
35+
use crate::services::reader::RoWallet;
36+
use crate::{DbHolder, DbUtxos};
3037

3138
pub enum Request2Writer {
3239
Consign(),
@@ -38,7 +45,7 @@ where
3845
Sp: Stockpile,
3946
Sp::Pile: Pile<Seal = TxoSeal>,
4047
{
41-
contracts: Contracts<Sp>,
48+
runtime: RgbRuntime<Owner<MultiResolver, DbHolder, XpubDerivable, DbUtxos>, Sp>,
4249
reader: USender<Request2Reader>,
4350
}
4451

@@ -48,18 +55,48 @@ where
4855
Sp::Stock: Send,
4956
Sp::Pile: Pile<Seal = TxoSeal> + Send,
5057
{
51-
pub fn new(stockpile: Sp, reader: USender<Request2Reader>) -> Self {
58+
pub fn new(network: Network, stockpile: Sp, reader: USender<Request2Reader>) -> Self {
5259
log::info!(target: Self::NAME, "Loading contracts from persistence");
53-
let me = Self { contracts: Contracts::load(stockpile), reader };
60+
let contracts = Contracts::load(stockpile);
61+
62+
// TODO: Use real resolver
63+
let resolver = MultiResolver::new_absent().unwrap_or_else(|err| {
64+
log::error!(target: Self::NAME, "Unable to connect to the resolver. {err}");
65+
panic!("Unable to connect to the resolver due to {err}");
66+
});
67+
68+
log::info!(target: Self::NAME, "Loading wallets from database");
69+
let holder = DbHolder::load().unwrap_or_else(|err| {
70+
log::error!(target: Self::NAME, "Unable to load database. {err}");
71+
panic!("Unable to load database due to {err}");
72+
});
73+
let owner = Owner::with_components(network, holder, resolver);
74+
75+
let runtime = RgbRuntime::with_components(owner, contracts);
76+
let mut me = Self { runtime, reader };
5477

5578
log::info!(target: Self::NAME, "Contracts loaded successfully, sending state to the reader");
56-
for id in me.contracts.contract_ids() {
57-
let state = me.contracts.contract_state(id);
79+
for id in me.runtime.contracts.contract_ids() {
80+
let state = me.runtime.contracts.contract_state(id);
5881
log::debug!(target: Self::NAME, "Sending contract state for {id}");
5982
me.reader
6083
.send(Request2Reader::UpsertContract(id, state))
6184
.unwrap_or_else(|err| panic!("Failed to send state for contract {id}: {err}"));
6285
}
86+
87+
log::info!(target: Self::NAME, "Wallets loaded successfully, sending state to the reader");
88+
let all_wallets = me.runtime.wallet.wallet_ids().collect::<HashSet<_>>();
89+
for id in all_wallets {
90+
me.runtime.wallet.switch(id);
91+
let descriptor = me.runtime.wallet.descriptor().clone();
92+
let utxo = me.runtime.wallet.utxos();
93+
let wallet = RoWallet { descriptor, utxos: utxo.utxos() };
94+
log::debug!(target: Self::NAME, "Sending wallet state for {id}");
95+
me.reader
96+
.send(Request2Reader::UpsertWallet(id, wallet))
97+
.unwrap_or_else(|err| panic!("Failed to send state for wallet {id}: {err}"));
98+
}
99+
63100
me
64101
}
65102
}

src/workers/broker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ where
9898
let reader_thread = UThread::new(reader, TIMEOUT);
9999

100100
log::info!("Starting contracts writer thread...");
101-
let writer = WriterService::new(stockpile, reader_thread.sender());
101+
let writer = WriterService::new(conf.network, stockpile, reader_thread.sender());
102102
let writer_thread = UThread::new(writer, TIMEOUT);
103103

104104
log::info!("Starting the dispatcher thread...");

0 commit comments

Comments
 (0)