RaptorQ fountain codes
This commit is contained in:
parent
ebfb619125
commit
dc573fc9a2
|
@ -68,6 +68,7 @@ rand_chacha = "0.3.1"
|
||||||
blake2b_simd = "1.0.0"
|
blake2b_simd = "1.0.0"
|
||||||
chacha20poly1305 = "0.9.0"
|
chacha20poly1305 = "0.9.0"
|
||||||
base64 = "^0.13"
|
base64 = "^0.13"
|
||||||
|
raptorq = "1.7.0"
|
||||||
|
|
||||||
ledger-apdu = { version = "0.9.0", optional = true }
|
ledger-apdu = { version = "0.9.0", optional = true }
|
||||||
hmac = { version = "0.12.1", optional = true }
|
hmac = { version = "0.12.1", optional = true }
|
||||||
|
|
10
binding.h
10
binding.h
|
@ -64,11 +64,9 @@ void scan_transparent_accounts(uint32_t gap_limit);
|
||||||
|
|
||||||
char *prepare_multi_payment(char *recipients_json, bool use_transparent, uint32_t anchor_offset);
|
char *prepare_multi_payment(char *recipients_json, bool use_transparent, uint32_t anchor_offset);
|
||||||
|
|
||||||
char *sign(char *tx_filename, int64_t port);
|
char *sign(char *tx, int64_t port);
|
||||||
|
|
||||||
char *broadcast(char *tx_filename);
|
char *broadcast_tx(char *tx_str);
|
||||||
|
|
||||||
char *broadcast_txhex(char *txhex);
|
|
||||||
|
|
||||||
uint32_t get_activation_date(void);
|
uint32_t get_activation_date(void);
|
||||||
|
|
||||||
|
@ -97,3 +95,7 @@ char *generate_random_enc_key(void);
|
||||||
char *get_full_backup(char *key);
|
char *get_full_backup(char *key);
|
||||||
|
|
||||||
char *restore_full_backup(char *key, char *backup);
|
char *restore_full_backup(char *key, char *backup);
|
||||||
|
|
||||||
|
char *split_data(uint32_t id, char *data);
|
||||||
|
|
||||||
|
char *merge_data(char *drop);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::coinconfig::{init_coin, CoinConfig};
|
use crate::coinconfig::{init_coin, CoinConfig};
|
||||||
use crate::{broadcast_tx, ChainError, Tx};
|
use crate::FountainCodes;
|
||||||
|
use crate::{ChainError, Tx};
|
||||||
use allo_isolate::{ffi, IntoDart};
|
use allo_isolate::{ffi, IntoDart};
|
||||||
use android_logger::Config;
|
use android_logger::Config;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
@ -72,21 +73,6 @@ fn log_string(result: anyhow::Result<String>) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_tx_result(res: anyhow::Result<Vec<u8>>) -> Vec<u8> {
|
|
||||||
let mut v = vec![];
|
|
||||||
match res {
|
|
||||||
Ok(raw_tx) => {
|
|
||||||
v.push(0x00);
|
|
||||||
v.extend(raw_tx);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
v.push(0x01);
|
|
||||||
v.extend(e.to_string().as_bytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn get_error() -> bool {
|
pub unsafe extern "C" fn get_error() -> bool {
|
||||||
IS_ERROR.load(Ordering::Acquire)
|
IS_ERROR.load(Ordering::Acquire)
|
||||||
|
@ -352,13 +338,10 @@ pub async unsafe extern "C" fn prepare_multi_payment(
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub async unsafe extern "C" fn sign(tx_filename: *mut c_char, port: i64) -> *mut c_char {
|
pub async unsafe extern "C" fn sign(tx: *mut c_char, port: i64) -> *mut c_char {
|
||||||
from_c_str!(tx_filename);
|
from_c_str!(tx);
|
||||||
let res = async {
|
let res = async {
|
||||||
let mut file = std::fs::File::open(&tx_filename.to_string())?;
|
let tx: Tx = serde_json::from_str(&tx)?;
|
||||||
let mut s = String::new();
|
|
||||||
file.read_to_string(&mut s)?;
|
|
||||||
let tx: Tx = serde_json::from_str(&s)?;
|
|
||||||
let raw_tx = crate::api::payment::sign_only_multi_payment(
|
let raw_tx = crate::api::payment::sign_only_multi_payment(
|
||||||
&tx,
|
&tx,
|
||||||
Box::new(move |progress| {
|
Box::new(move |progress| {
|
||||||
|
@ -366,33 +349,19 @@ pub async unsafe extern "C" fn sign(tx_filename: *mut c_char, port: i64) -> *mut
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(raw_tx)
|
let tx_str = base64::encode(&raw_tx);
|
||||||
};
|
Ok(tx_str)
|
||||||
let tx_hex = hex::encode(encode_tx_result(res.await));
|
|
||||||
to_c_str(tx_hex)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
#[no_mangle]
|
|
||||||
pub async unsafe extern "C" fn broadcast(tx_filename: *mut c_char) -> *mut c_char {
|
|
||||||
from_c_str!(tx_filename);
|
|
||||||
let res = async {
|
|
||||||
let mut file = std::fs::File::open(&tx_filename.to_string())?;
|
|
||||||
let mut s = String::new();
|
|
||||||
file.read_to_string(&mut s)?;
|
|
||||||
let tx = hex::decode(s.trim_end())?;
|
|
||||||
broadcast_tx(&tx).await
|
|
||||||
};
|
};
|
||||||
to_c_str(log_string(res.await))
|
to_c_str(log_string(res.await))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub async unsafe extern "C" fn broadcast_txhex(txhex: *mut c_char) -> *mut c_char {
|
pub async unsafe extern "C" fn broadcast_tx(tx_str: *mut c_char) -> *mut c_char {
|
||||||
from_c_str!(txhex);
|
from_c_str!(tx_str);
|
||||||
let res = async {
|
let res = async {
|
||||||
let tx = hex::decode(&txhex.to_string())?;
|
let tx = base64::decode(&*tx_str)?;
|
||||||
broadcast_tx(&tx).await
|
crate::broadcast_tx(&tx).await
|
||||||
};
|
};
|
||||||
to_c_str(log_string(res.await))
|
to_c_str(log_string(res.await))
|
||||||
}
|
}
|
||||||
|
@ -523,3 +492,32 @@ pub unsafe extern "C" fn restore_full_backup(key: *mut c_char, backup: *mut c_ch
|
||||||
};
|
};
|
||||||
to_c_str(log_string(res()))
|
to_c_str(log_string(res()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn split_data(id: u32, data: *mut c_char) -> *mut c_char {
|
||||||
|
from_c_str!(data);
|
||||||
|
let res = || {
|
||||||
|
let res = crate::FountainCodes::encode_into_drops(id, &base64::decode(&*data)?)?;
|
||||||
|
let output = serde_json::to_string(&res)?;
|
||||||
|
Ok(output)
|
||||||
|
};
|
||||||
|
to_c_str(log_string(res()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn merge_data(drop: *mut c_char) -> *mut c_char {
|
||||||
|
from_c_str!(drop);
|
||||||
|
let res = || {
|
||||||
|
let res = crate::put_drop(&*drop)?
|
||||||
|
.map(|d| base64::encode(&d))
|
||||||
|
.unwrap_or(String::new());
|
||||||
|
Ok::<_, anyhow::Error>(res)
|
||||||
|
};
|
||||||
|
match res() {
|
||||||
|
Ok(str) => to_c_str(str),
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{}", e);
|
||||||
|
to_c_str(String::new()) // do not return error msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter, MemPool};
|
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter, FountainCodes, MemPool};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use lazycell::AtomicLazyCell;
|
use lazycell::AtomicLazyCell;
|
||||||
|
@ -15,6 +15,7 @@ lazy_static! {
|
||||||
Mutex::new(CoinConfig::new(1, CoinType::Ycash)),
|
Mutex::new(CoinConfig::new(1, CoinType::Ycash)),
|
||||||
];
|
];
|
||||||
pub static ref PROVER: AtomicLazyCell<LocalTxProver> = AtomicLazyCell::new();
|
pub static ref PROVER: AtomicLazyCell<LocalTxProver> = AtomicLazyCell::new();
|
||||||
|
pub static ref RAPTORQ: Mutex<FountainCodes> = Mutex::new(FountainCodes::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static ACTIVE_COIN: AtomicU8 = AtomicU8::new(0);
|
pub static ACTIVE_COIN: AtomicU8 = AtomicU8::new(0);
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
use crate::coinconfig::RAPTORQ;
|
||||||
|
use blake2b_simd::Params;
|
||||||
|
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
|
||||||
|
use raptorq::{Decoder, Encoder, EncodingPacket, ObjectTransmissionInformation};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::borrow::BorrowMut;
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::io::{Cursor, Write};
|
||||||
|
|
||||||
|
pub const QR_DATA_SIZE: u16 = 256;
|
||||||
|
|
||||||
|
pub struct FountainCodes {
|
||||||
|
id: u32,
|
||||||
|
decoder: Option<Decoder>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct RaptorQDrops {
|
||||||
|
drops: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FountainCodes {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
FountainCodes {
|
||||||
|
id: 0,
|
||||||
|
decoder: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_into_drops(id: u32, data: &[u8]) -> anyhow::Result<RaptorQDrops> {
|
||||||
|
let total_length = data.len() as u32;
|
||||||
|
let encoder = Encoder::with_defaults(data, QR_DATA_SIZE);
|
||||||
|
let drops: Vec<_> = encoder
|
||||||
|
.get_encoded_packets(1)
|
||||||
|
.iter()
|
||||||
|
.map(|p| {
|
||||||
|
let mut result = vec![];
|
||||||
|
let data = p.serialize();
|
||||||
|
let checksum = Self::get_checksum(&data, id, total_length);
|
||||||
|
result.write_u32::<LE>(id).unwrap();
|
||||||
|
result.write_u32::<LE>(total_length as u32).unwrap();
|
||||||
|
result.write_u32::<LE>(checksum).unwrap();
|
||||||
|
result.write_all(&data).unwrap();
|
||||||
|
base64::encode(&result)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(RaptorQDrops { drops })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_drop(&mut self, drop: &str) -> anyhow::Result<Option<Vec<u8>>> {
|
||||||
|
let drop = base64::decode(drop)?;
|
||||||
|
if drop.len() < 12 {
|
||||||
|
anyhow::bail!("Not enough data");
|
||||||
|
}
|
||||||
|
let (header, data) = drop.split_at(12);
|
||||||
|
let mut c = Cursor::new(header);
|
||||||
|
let id = c.read_u32::<LE>()?;
|
||||||
|
let total_length = c.read_u32::<LE>()?;
|
||||||
|
let checksum = c.read_u32::<LE>()?;
|
||||||
|
let checksum2 = Self::get_checksum(data, id, total_length);
|
||||||
|
if checksum != checksum2 {
|
||||||
|
anyhow::bail!("Invalid checksum");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.id != id {
|
||||||
|
self.id = id;
|
||||||
|
let decoder = Decoder::new(ObjectTransmissionInformation::with_defaults(
|
||||||
|
total_length as u64,
|
||||||
|
QR_DATA_SIZE,
|
||||||
|
));
|
||||||
|
self.decoder = Some(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref mut decoder) = self.decoder {
|
||||||
|
let res = decoder.decode(EncodingPacket::deserialize(data));
|
||||||
|
if res.is_some() {
|
||||||
|
self.id = 0;
|
||||||
|
self.decoder = None;
|
||||||
|
}
|
||||||
|
return Ok(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_checksum(data: &[u8], id: u32, total_length: u32) -> u32 {
|
||||||
|
let hash = Params::new()
|
||||||
|
.personal(b"QR_CHECKSUM")
|
||||||
|
.hash_length(4)
|
||||||
|
.to_state()
|
||||||
|
.update(&id.to_le_bytes())
|
||||||
|
.update(&total_length.to_le_bytes())
|
||||||
|
.update(data)
|
||||||
|
.finalize();
|
||||||
|
let h = u32::from_le_bytes(hash.as_bytes().try_into().unwrap());
|
||||||
|
h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn put_drop(drop: &str) -> anyhow::Result<Option<Vec<u8>>> {
|
||||||
|
let mut fc = RAPTORQ.lock().unwrap();
|
||||||
|
fc.put_drop(drop)
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ mod coinconfig;
|
||||||
mod commitment;
|
mod commitment;
|
||||||
mod contact;
|
mod contact;
|
||||||
mod db;
|
mod db;
|
||||||
|
mod fountain;
|
||||||
mod hash;
|
mod hash;
|
||||||
mod key;
|
mod key;
|
||||||
mod key2;
|
mod key2;
|
||||||
|
@ -69,6 +70,7 @@ pub use crate::coinconfig::{
|
||||||
};
|
};
|
||||||
pub use crate::commitment::{CTree, Witness};
|
pub use crate::commitment::{CTree, Witness};
|
||||||
pub use crate::db::{AccountRec, DbAdapter, TxRec};
|
pub use crate::db::{AccountRec, DbAdapter, TxRec};
|
||||||
|
pub use crate::fountain::{put_drop, FountainCodes, RaptorQDrops};
|
||||||
pub use crate::hash::pedersen_hash;
|
pub use crate::hash::pedersen_hash;
|
||||||
pub use crate::key::{generate_random_enc_key, KeyHelpers};
|
pub use crate::key::{generate_random_enc_key, KeyHelpers};
|
||||||
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::collections::HashMap;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use warp_api_ffi::api::payment::{Recipient, RecipientMemo};
|
use warp_api_ffi::api::payment::{Recipient, RecipientMemo};
|
||||||
use warp_api_ffi::api::payment_uri::PaymentURI;
|
use warp_api_ffi::api::payment_uri::PaymentURI;
|
||||||
use warp_api_ffi::{AccountRec, CoinConfig, Tx, TxRec};
|
use warp_api_ffi::{AccountRec, CoinConfig, RaptorQDrops, Tx, TxRec};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -78,6 +78,8 @@ async fn main() -> anyhow::Result<()> {
|
||||||
new_diversified_address,
|
new_diversified_address,
|
||||||
make_payment_uri,
|
make_payment_uri,
|
||||||
parse_payment_uri,
|
parse_payment_uri,
|
||||||
|
split_data,
|
||||||
|
merge_data,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.attach(AdHoc::config::<Config>())
|
.attach(AdHoc::config::<Config>())
|
||||||
|
@ -267,6 +269,20 @@ pub fn parse_payment_uri(uri: String) -> Result<Json<PaymentURI>, Error> {
|
||||||
Ok(Json(payment))
|
Ok(Json(payment))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[get("/split?<id>&<data>")]
|
||||||
|
pub fn split_data(id: u32, data: String) -> Result<Json<RaptorQDrops>, Error> {
|
||||||
|
let result = warp_api_ffi::FountainCodes::encode_into_drops(id, &hex::decode(data).unwrap())?;
|
||||||
|
Ok(Json(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/merge?<data>")]
|
||||||
|
pub fn merge_data(data: String) -> Result<String, Error> {
|
||||||
|
let result = warp_api_ffi::put_drop(&data)?
|
||||||
|
.map(|data| hex::encode(&data))
|
||||||
|
.unwrap_or(String::new());
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
|
|
@ -305,6 +305,7 @@ impl Tx {
|
||||||
let chain = get_coin_chain(self.coin_type);
|
let chain = get_coin_chain(self.coin_type);
|
||||||
let last_height = BlockHeight::from_u32(self.height as u32);
|
let last_height = BlockHeight::from_u32(self.height as u32);
|
||||||
let mut builder = Builder::new(*chain.network(), last_height);
|
let mut builder = Builder::new(*chain.network(), last_height);
|
||||||
|
let efvk = ExtendedFullViewingKey::from(zsk);
|
||||||
|
|
||||||
let ovk = hex_to_hash(&self.ovk)?;
|
let ovk = hex_to_hash(&self.ovk)?;
|
||||||
builder.send_change_to(
|
builder.send_change_to(
|
||||||
|
@ -340,6 +341,9 @@ impl Tx {
|
||||||
&txin.fvk,
|
&txin.fvk,
|
||||||
)?
|
)?
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
if fvk != efvk {
|
||||||
|
anyhow::bail!("Incorrect account - Secret key mismatch")
|
||||||
|
}
|
||||||
let pa = fvk.fvk.vk.to_payment_address(diversifier).unwrap();
|
let pa = fvk.fvk.vk.to_payment_address(diversifier).unwrap();
|
||||||
let mut rseed_bytes = [0u8; 32];
|
let mut rseed_bytes = [0u8; 32];
|
||||||
hex::decode_to_slice(&txin.rseed, &mut rseed_bytes)?;
|
hex::decode_to_slice(&txin.rseed, &mut rseed_bytes)?;
|
||||||
|
|
Loading…
Reference in New Issue