1#![no_std]
10#![deny(missing_docs)]
11#![deny(rustdoc::broken_intra_doc_links)]
12#![deny(clippy::pedantic)]
13#![allow(clippy::module_name_repetitions)]
14#![deny(unused_crate_dependencies)]
15#![deny(unused_extern_crates)]
16
17extern crate alloc;
18
19use alloc::format;
20use alloc::string::{String, ToString};
21use alloc::vec::Vec;
22
23use dusk_core::BlsScalar;
24use dusk_core::abi::ContractId;
25use dusk_core::signatures::bls::PublicKey as AccountPublicKey;
26use dusk_core::transfer::moonlight::AccountData;
27use dusk_core::transfer::withdraw::Withdraw;
28use dusk_core::transfer::{
29 CONTRACT_TO_ACCOUNT_TOPIC, CONTRACT_TO_CONTRACT_TOPIC, CONVERT_TOPIC,
30 ContractToAccount, ContractToAccountEvent, ContractToContract,
31 ContractToContractEvent, ConvertEvent, DEPOSIT_TOPIC, DepositEvent,
32 MINT_CONTRACT_TOPIC, MINT_TOPIC, MOONLIGHT_TOPIC,
33 MoonlightTransactionEvent, PHOENIX_TOPIC, PhoenixTransactionEvent,
34 WITHDRAW_TOPIC, WithdrawEvent,
35};
36use dusk_data_driver::{
37 ConvertibleContract, Error, JsonValue, from_rkyv, json_to_rkyv,
38 json_to_rkyv_pair_u64, json_to_rkyv_u64, rkyv_to_json,
39 rkyv_to_json_pair_u64, rkyv_to_json_u64, to_json,
40};
41
42#[derive(Default)]
44pub struct ContractDriver;
45
46const TRANSFER_SCHEMA_FUNCTIONS: &str = r#"[{"name":"convert","doc":"","input":"Withdraw","output":"()","custom":false},{"name":"deposit","doc":"","input":"u64","output":"()","custom":false},{"name":"mint","doc":"","input":"Withdraw","output":"()","custom":false},{"name":"withdraw","doc":"","input":"Withdraw","output":"()","custom":false},{"name":"mint_to_contract","doc":"","input":"ContractToContract","output":"()","custom":false},{"name":"contract_to_contract","doc":"","input":"ContractToContract","output":"()","custom":false},{"name":"contract_to_account","doc":"","input":"ContractToAccount","output":"()","custom":false},{"name":"root","doc":"","input":"()","output":"BlsScalar","custom":false},{"name":"num_notes","doc":"","input":"()","output":"u64","custom":false},{"name":"chain_id","doc":"","input":"()","output":"u8","custom":false},{"name":"account","doc":"","input":"AccountPublicKey","output":"AccountData","custom":false},{"name":"contract_balance","doc":"","input":"ContractId","output":"u64","custom":false},{"name":"opening","doc":"","input":"u64","output":"unsupported","custom":false},{"name":"existing_nullifiers","doc":"","input":"Vec<BlsScalar>","output":"Vec<BlsScalar>","custom":false},{"name":"leaves_from_height","doc":"","input":"u64","output":"unsupported","custom":false},{"name":"leaves_from_pos","doc":"","input":"u64","output":"unsupported","custom":false},{"name":"sync","doc":"","input":"(u64,u64)","output":"unsupported","custom":false},{"name":"sync_nullifiers","doc":"","input":"(u64,u64)","output":"BlsScalar","custom":false},{"name":"sync_contract_balances","doc":"","input":"(u64,u64)","output":"(ContractId,u64)","custom":false},{"name":"sync_accounts","doc":"","input":"(u64,u64)","output":"(AccountData,AccountPublicKey)","custom":false}]"#;
47
48#[allow(clippy::match_same_arms)]
49impl ConvertibleContract for ContractDriver {
50 fn encode_input_fn(
51 &self,
52 fn_name: &str,
53 json: &str,
54 ) -> Result<Vec<u8>, Error> {
55 match fn_name {
56 "convert" => json_to_rkyv::<Withdraw>(json),
58 "deposit" => json_to_rkyv_u64(json),
60 "mint" | "withdraw" => json_to_rkyv::<Withdraw>(json),
61 "mint_to_contract" | "contract_to_contract" => {
62 json_to_rkyv::<ContractToContract>(json)
63 }
64 "contract_to_account" => json_to_rkyv::<ContractToAccount>(json),
65 "root" | "num_notes" | "chain_id" => json_to_rkyv::<()>(json),
67 "account" => json_to_rkyv::<AccountPublicKey>(json),
68 "contract_balance" => json_to_rkyv::<ContractId>(json),
69 "opening" => json_to_rkyv_u64(json),
70 "existing_nullifiers" => json_to_rkyv::<Vec<BlsScalar>>(json),
71 "leaves_from_height" | "leaves_from_pos" => json_to_rkyv_u64(json),
73 "sync"
74 | "sync_nullifiers"
75 | "sync_contract_balances"
76 | "sync_accounts" => json_to_rkyv_pair_u64(json),
77
78 name => Err(Error::Unsupported(format!("fn_name {name}"))),
80 }
81 }
82
83 fn decode_input_fn(
84 &self,
85 fn_name: &str,
86 rkyv: &[u8],
87 ) -> Result<JsonValue, Error> {
88 match fn_name {
89 "convert" => rkyv_to_json::<Withdraw>(rkyv),
91 "deposit" => rkyv_to_json_u64(rkyv),
93 "mint" | "withdraw" => rkyv_to_json::<Withdraw>(rkyv),
94 "mint_to_contract" | "contract_to_contract" => {
95 rkyv_to_json::<ContractToContract>(rkyv)
96 }
97 "contract_to_account" => rkyv_to_json::<ContractToAccount>(rkyv),
98 "root" | "num_notes" | "chain_id" => rkyv_to_json::<()>(rkyv),
100 "account" => rkyv_to_json::<AccountPublicKey>(rkyv),
101 "contract_balance" => rkyv_to_json::<ContractId>(rkyv),
102 "opening" => rkyv_to_json_u64(rkyv),
103 "existing_nullifiers" => rkyv_to_json::<Vec<BlsScalar>>(rkyv),
104 "leaves_from_height" | "leaves_from_pos" => rkyv_to_json_u64(rkyv),
106 "sync"
107 | "sync_nullifiers"
108 | "sync_contract_balances"
109 | "sync_accounts" => rkyv_to_json_pair_u64(rkyv),
110
111 name => Err(Error::Unsupported(format!("fn_name {name}"))),
113 }
114 }
115
116 fn decode_output_fn(
117 &self,
118 fn_name: &str,
119 rkyv: &[u8],
120 ) -> Result<JsonValue, Error> {
121 match fn_name {
122 "convert" => Ok(JsonValue::Null),
124 "deposit" => Ok(JsonValue::Null),
126 "mint" | "withdraw" => Ok(JsonValue::Null),
127 "mint_to_contract" | "contract_to_contract" => Ok(JsonValue::Null),
128 "contract_to_account" => Ok(JsonValue::Null),
129 "root" => rkyv_to_json::<BlsScalar>(rkyv),
131 "num_notes" => rkyv_to_json_u64(rkyv),
132 "chain_id" => rkyv_to_json::<u8>(rkyv),
133 "account" => rkyv_to_json::<AccountData>(rkyv),
134 "contract_balance" => rkyv_to_json_u64(rkyv),
135 "opening" => {
136 Err(Error::Unsupported("opening not supported".into()))
137 }
138 "existing_nullifiers" => rkyv_to_json::<Vec<BlsScalar>>(rkyv),
139 "sync_accounts" => from_rkyv::<(AccountData, [u8; 193])>(rkyv)
141 .and_then(|(data, key)| {
142 let key =
146 unsafe { AccountPublicKey::from_slice_unchecked(&key) };
147 if !key.is_valid() {
148 return Err(Error::Other(
149 "invalid account public key".into(),
150 ));
151 }
152
153 to_json((data, key)).map_err(Error::from)
154 }),
155 "sync_nullifiers" => rkyv_to_json::<BlsScalar>(rkyv),
156 "sync_contract_balances" => from_rkyv::<(ContractId, u64)>(rkyv)
157 .and_then(|(contract, balance)| {
158 to_json((contract, JsonValue::String(balance.to_string())))
159 .map_err(Error::from)
160 }),
161
162 "leaves_from_height" | "leaves_from_pos" | "sync" => {
163 Err(Error::Unsupported("unsupported".into()))
164 }
165
166 name => Err(Error::Unsupported(format!("fn_name {name}"))),
168 }
169 }
170
171 fn decode_event(
172 &self,
173 event_name: &str,
174 rkyv: &[u8],
175 ) -> Result<JsonValue, Error> {
176 match event_name {
177 MOONLIGHT_TOPIC => rkyv_to_json::<MoonlightTransactionEvent>(rkyv),
178 PHOENIX_TOPIC => rkyv_to_json::<PhoenixTransactionEvent>(rkyv),
179 CONTRACT_TO_CONTRACT_TOPIC | MINT_CONTRACT_TOPIC => {
180 rkyv_to_json::<ContractToContractEvent>(rkyv)
181 }
182 CONTRACT_TO_ACCOUNT_TOPIC => {
183 rkyv_to_json::<ContractToAccountEvent>(rkyv)
184 }
185 WITHDRAW_TOPIC | MINT_TOPIC => rkyv_to_json::<WithdrawEvent>(rkyv),
186 DEPOSIT_TOPIC => rkyv_to_json::<DepositEvent>(rkyv),
187 CONVERT_TOPIC => rkyv_to_json::<ConvertEvent>(rkyv),
188
189 event => Err(Error::Unsupported(format!("event {event}"))),
190 }
191 }
192
193 fn get_schema(&self) -> String {
194 let moonlight = MOONLIGHT_TOPIC;
195 let phoenix = PHOENIX_TOPIC;
196 let contract_to_contract = CONTRACT_TO_CONTRACT_TOPIC;
197 let mint_contract = MINT_CONTRACT_TOPIC;
198 let contract_to_account = CONTRACT_TO_ACCOUNT_TOPIC;
199 let withdraw = WITHDRAW_TOPIC;
200 let mint = MINT_TOPIC;
201 let deposit = DEPOSIT_TOPIC;
202 let convert = CONVERT_TOPIC;
203
204 let events = format!(
205 r#"[{{"topic":"{moonlight}","data":"MoonlightTransactionEvent"}},{{"topic":"{phoenix}","data":"PhoenixTransactionEvent"}},{{"topic":"{contract_to_contract}","data":"ContractToContractEvent"}},{{"topic":"{mint_contract}","data":"ContractToContractEvent"}},{{"topic":"{contract_to_account}","data":"ContractToAccountEvent"}},{{"topic":"{withdraw}","data":"WithdrawEvent"}},{{"topic":"{mint}","data":"WithdrawEvent"}},{{"topic":"{deposit}","data":"DepositEvent"}},{{"topic":"{convert}","data":"ConvertEvent"}}]"#
206 );
207 let functions = TRANSFER_SCHEMA_FUNCTIONS;
208
209 format!(
210 r#"{{"name":"TransferContract","imports":[],"functions":{functions},"events":{events}}}"#
211 )
212 }
213}
214
215#[cfg(all(target_family = "wasm", feature = "ffi"))]
216dusk_data_driver::generate_wasm_entrypoint!(ContractDriver);