RaptorQ fountain codes

This commit is contained in:
Hanh 2022-06-20 17:05:11 +08:00
parent ebfb619125
commit dc573fc9a2
8 changed files with 175 additions and 48 deletions

View File

@ -68,6 +68,7 @@ rand_chacha = "0.3.1"
blake2b_simd = "1.0.0"
chacha20poly1305 = "0.9.0"
base64 = "^0.13"
raptorq = "1.7.0"
ledger-apdu = { version = "0.9.0", optional = true }
hmac = { version = "0.12.1", optional = true }

View File

@ -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 *sign(char *tx_filename, int64_t port);
char *sign(char *tx, int64_t port);
char *broadcast(char *tx_filename);
char *broadcast_txhex(char *txhex);
char *broadcast_tx(char *tx_str);
uint32_t get_activation_date(void);
@ -97,3 +95,7 @@ char *generate_random_enc_key(void);
char *get_full_backup(char *key);
char *restore_full_backup(char *key, char *backup);
char *split_data(uint32_t id, char *data);
char *merge_data(char *drop);

View File

@ -1,5 +1,6 @@
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 android_logger::Config;
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]
pub unsafe extern "C" fn get_error() -> bool {
IS_ERROR.load(Ordering::Acquire)
@ -352,13 +338,10 @@ pub async unsafe extern "C" fn prepare_multi_payment(
#[tokio::main]
#[no_mangle]
pub async unsafe extern "C" fn sign(tx_filename: *mut c_char, port: i64) -> *mut c_char {
from_c_str!(tx_filename);
pub async unsafe extern "C" fn sign(tx: *mut c_char, port: i64) -> *mut c_char {
from_c_str!(tx);
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: Tx = serde_json::from_str(&s)?;
let tx: Tx = serde_json::from_str(&tx)?;
let raw_tx = crate::api::payment::sign_only_multi_payment(
&tx,
Box::new(move |progress| {
@ -366,33 +349,19 @@ pub async unsafe extern "C" fn sign(tx_filename: *mut c_char, port: i64) -> *mut
}),
)
.await?;
Ok(raw_tx)
};
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
let tx_str = base64::encode(&raw_tx);
Ok(tx_str)
};
to_c_str(log_string(res.await))
}
#[tokio::main]
#[no_mangle]
pub async unsafe extern "C" fn broadcast_txhex(txhex: *mut c_char) -> *mut c_char {
from_c_str!(txhex);
pub async unsafe extern "C" fn broadcast_tx(tx_str: *mut c_char) -> *mut c_char {
from_c_str!(tx_str);
let res = async {
let tx = hex::decode(&txhex.to_string())?;
broadcast_tx(&tx).await
let tx = base64::decode(&*tx_str)?;
crate::broadcast_tx(&tx).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()))
}
#[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
}
}
}

View File

@ -1,4 +1,4 @@
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter, MemPool};
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter, FountainCodes, MemPool};
use anyhow::anyhow;
use lazy_static::lazy_static;
use lazycell::AtomicLazyCell;
@ -15,6 +15,7 @@ lazy_static! {
Mutex::new(CoinConfig::new(1, CoinType::Ycash)),
];
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);

103
src/fountain.rs Normal file
View File

@ -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)
}

View File

@ -25,6 +25,7 @@ mod coinconfig;
mod commitment;
mod contact;
mod db;
mod fountain;
mod hash;
mod key;
mod key2;
@ -69,6 +70,7 @@ pub use crate::coinconfig::{
};
pub use crate::commitment::{CTree, Witness};
pub use crate::db::{AccountRec, DbAdapter, TxRec};
pub use crate::fountain::{put_drop, FountainCodes, RaptorQDrops};
pub use crate::hash::pedersen_hash;
pub use crate::key::{generate_random_enc_key, KeyHelpers};
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;

View File

@ -11,7 +11,7 @@ use std::collections::HashMap;
use thiserror::Error;
use warp_api_ffi::api::payment::{Recipient, RecipientMemo};
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)]
pub enum Error {
@ -78,6 +78,8 @@ async fn main() -> anyhow::Result<()> {
new_diversified_address,
make_payment_uri,
parse_payment_uri,
split_data,
merge_data,
],
)
.attach(AdHoc::config::<Config>())
@ -267,6 +269,20 @@ pub fn parse_payment_uri(uri: String) -> Result<Json<PaymentURI>, Error> {
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)]
#[serde(crate = "rocket::serde")]
pub struct Config {

View File

@ -305,6 +305,7 @@ impl Tx {
let chain = get_coin_chain(self.coin_type);
let last_height = BlockHeight::from_u32(self.height as u32);
let mut builder = Builder::new(*chain.network(), last_height);
let efvk = ExtendedFullViewingKey::from(zsk);
let ovk = hex_to_hash(&self.ovk)?;
builder.send_change_to(
@ -340,6 +341,9 @@ impl Tx {
&txin.fvk,
)?
.unwrap();
if fvk != efvk {
anyhow::bail!("Incorrect account - Secret key mismatch")
}
let pa = fvk.fvk.vk.to_payment_address(diversifier).unwrap();
let mut rseed_bytes = [0u8; 32];
hex::decode_to_slice(&txin.rseed, &mut rseed_bytes)?;