Milestone 1

This commit is contained in:
scilio 2021-11-12 11:09:11 -05:00
parent 7e5ddfc849
commit 9f3a533504
11 changed files with 3209 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
*.swp
.DS_Store
target
*/Cargo.lock
*.iml
.idea/
.vscode/

1652
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

28
Cargo.toml Normal file
View file

@ -0,0 +1,28 @@
[package]
name = "mwixnet"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
blake2 = { package = "blake2-rfc", version = "0.2"}
byteorder = "1"
bytes = "0.5.6"
chacha20 = "0.8.1"
failure = "0.1.8"
futures = "0.3"
hmac = { version = "0.11.0", features = ["std"]}
hyper = { version = "0.14", features = ["full"] }
jsonrpc-core = "18.0"
jsonrpc-derive = "18.0"
jsonrpc-http-server = "18.0"
lazy_static = "1"
rand = "0.8.4"
serde = { version = "1", features= ["derive"]}
serde_derive = "1"
serde_json = "1"
sha2 = "0.9.8"
tokio = { version = "1", features = ["full"] }
grin_secp256k1zkp = { version = "0.7.11", features = ["bullet-proof-sizing"]}
grin_util = "5"

21
README.md Normal file
View file

@ -0,0 +1,21 @@
# MWixnet
MW CoinSwap Server
## APIs
### swap
The server configured to be the entry server (node 1) exposes a JSON-RPC `swap` API for use by GRIN wallets.
**jsonrpc:** `2.0`
**method:** `swap`
**params:**
```
[{
"comsig": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f",
"msg": "00010203",
"onion": {
"commit": "0967593792bc958cd73848c0b948ecab2c6e996ab3c550d462fe41359e447b651f",
"data": ["3719e5fba260c71a5a4bcf9d9caa58cd5dc49531388782fae7699c6fa6b30b09fe42"],
"pubkey": "020dd38a220280f14515f6901a3a366cb7b87630814e4b68b3189a32df964961e5"
}
}]
```

110
src/error.rs Normal file
View file

@ -0,0 +1,110 @@
use failure::{self, Context, Fail};
use std::fmt::{self, Display};
use std::io;
/// MWixnet error definition
#[derive(Debug)]
pub struct Error {
inner: Context<ErrorKind>,
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Debug, Eq, Fail, PartialEq)]
/// MWixnet error types
pub enum ErrorKind {
/// Unsupported payload version
#[fail(display = "Unsupported Payload Version")]
UnsupportedPayload,
/// Error from secp256k1-zkp library
#[fail(display = "Secp Error")]
SecpError,
/// Invalid key length for MAC initialization
#[fail(display = "InvalidKeyLength")]
InvalidKeyLength,
/// Wraps an io error produced when reading or writing
#[fail(display = "IOError")]
IOErr(
String,
io::ErrorKind,
),
/// Expected a given value that wasn't found
#[fail(display = "UnexpectedData")]
UnexpectedData {
/// What we wanted
expected: Vec<u8>,
/// What we got
received: Vec<u8>,
},
/// Data wasn't in a consumable format
#[fail(display = "CorruptedData")]
CorruptedData,
/// Incorrect number of elements (when deserializing a vec via read_multi say).
#[fail(display = "CountError")]
CountError,
/// When asked to read too much data
#[fail(display = "TooLargeReadErr")]
TooLargeReadErr,
}
impl std::error::Error for Error {
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
ErrorKind::IOErr(format!("{}", e), e.kind()).into()
}
}
impl From<io::ErrorKind> for Error {
fn from(e: io::ErrorKind) -> Error {
ErrorKind::IOErr(format!("{}", io::Error::from(e)), e).into()
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.inner, f)
}
}
impl Error {
pub fn kind(&self) -> ErrorKind {
self.inner.get_context().clone()
}
pub fn message(&self) -> String {
format!("{}", self).into()
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error {
inner: Context::new(kind),
}
}
}
impl From<Context<ErrorKind>> for Error {
fn from(inner: Context<ErrorKind>) -> Error {
Error { inner }
}
}
impl From<secp256k1zkp::Error> for Error {
fn from(_error: secp256k1zkp::Error) -> Error {
Error {
inner: Context::new(ErrorKind::SecpError),
}
}
}
impl From<hmac::crypto_mac::InvalidKeyLength> for Error {
fn from(_error: hmac::crypto_mac::InvalidKeyLength) -> Error {
Error {
inner: Context::new(ErrorKind::InvalidKeyLength),
}
}
}

29
src/main.rs Normal file
View file

@ -0,0 +1,29 @@
use server::ServerConfig;
#[macro_use]
extern crate lazy_static;
mod error;
mod onion;
mod secp;
mod ser;
mod server;
mod types;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let secret_key = secp::insecure_rand_secret()?; // todo - load from encrypted key file
let server_config = ServerConfig {
key: secret_key,
addr: "127.0.0.1:3000".parse().unwrap(),
is_first: true
};
let shutdown_signal = async move {
// Wait for the CTRL+C signal
tokio::signal::ctrl_c()
.await
.expect("failed to install CTRL+C signal handler");
};
server::listen(&server_config, shutdown_signal)
}

167
src/onion.rs Normal file
View file

@ -0,0 +1,167 @@
use crate::error::Result;
use crate::secp::{self, Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret};
use crate::types::{Hop, Onion, RawBytes, Payload, deserialize_payload, serialize_payload};
use crate::ser;
use chacha20::{ChaCha20, Key, Nonce};
use chacha20::cipher::{NewCipher, StreamCipher};
use hmac::{Hmac, Mac, NewMac};
use sha2::{Digest, Sha256};
type HmacSha256 = Hmac<Sha256>;
/// Create an Onion for the Commitment, encrypting the payload for each hop
pub fn create_onion(commitment: &Commitment, session_key: &SecretKey, hops: &Vec<Hop>) -> Result<Onion> {
let secp = Secp256k1::new();
let mut ephemeral_key = session_key.clone();
let mut shared_secrets: Vec<SharedSecret> = Vec::new();
let mut enc_payloads: Vec<RawBytes> = Vec::new();
for hop in hops {
let shared_secret = SharedSecret::new(&secp, &hop.pubkey, &ephemeral_key);
let ephemeral_pubkey = PublicKey::from_secret_key(&secp, &ephemeral_key)?;
let blinding_factor = calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?;
shared_secrets.push(shared_secret);
enc_payloads.push(serialize_payload(&hop.payload)?);
ephemeral_key.mul_assign(&secp, &blinding_factor)?;
}
for i in (0..shared_secrets.len()).rev() {
let mut cipher = new_stream_cipher(&shared_secrets[i])?;
for j in i..shared_secrets.len() {
cipher.apply_keystream(&mut enc_payloads[j]);
}
}
let onion = Onion{
ephemeral_pubkey: secp::to_public_key(&session_key)?,
commit: commitment.clone(),
enc_payloads: enc_payloads,
};
Ok(onion)
}
/// Peel a single layer off of the Onion, returning the peeled Onion and decrypted Payload
pub fn peel_layer(onion: &Onion, secret_key: &SecretKey) -> Result<(Payload, Onion)> {
let secp = Secp256k1::new();
let shared_secret = SharedSecret::new(&secp, &onion.ephemeral_pubkey, &secret_key);
let mut cipher = new_stream_cipher(&shared_secret)?;
let mut decrypted_bytes = onion.enc_payloads[0].clone();
cipher.apply_keystream(&mut decrypted_bytes);
let decrypted_payload = deserialize_payload(&decrypted_bytes)?;
let enc_payloads : Vec<RawBytes> = onion.enc_payloads.iter()
.enumerate()
.filter(|&(i, _)| i != 0)
.map(|(_, enc_payload)| {
let mut p = enc_payload.clone();
cipher.apply_keystream(&mut p);
p
})
.collect();
let blinding_factor = calc_blinding_factor(&shared_secret, &onion.ephemeral_pubkey)?;
let mut ephemeral_pubkey = onion.ephemeral_pubkey.clone();
ephemeral_pubkey.mul_assign(&secp, &blinding_factor)?;
let mut commitment = onion.commit.clone();
commitment = secp::add_excess(&commitment, &decrypted_payload.excess)?;
let peeled_onion = Onion{
ephemeral_pubkey: ephemeral_pubkey,
commit: commitment.clone(),
enc_payloads: enc_payloads,
};
Ok((decrypted_payload, peeled_onion))
}
fn calc_blinding_factor(shared_secret: &SharedSecret, ephemeral_pubkey: &PublicKey) -> Result<SecretKey> {
let serialized_pubkey = ser::ser_vec(&ephemeral_pubkey)?;
let mut hasher = Sha256::default();
hasher.update(&serialized_pubkey);
hasher.update(&shared_secret[0..32]);
let secp = Secp256k1::new();
let blind = SecretKey::from_slice(&secp, &hasher.finalize())?;
Ok(blind)
}
fn new_stream_cipher(shared_secret: &SharedSecret) -> Result<ChaCha20> {
let mut mu_hmac = HmacSha256::new_from_slice(b"PAYLOAD")?;
mu_hmac.update(&shared_secret[0..32]);
let mukey = mu_hmac.finalize().into_bytes();
let key = Key::from_slice(&mukey[0..32]);
let nonce = Nonce::from_slice(b"NONCE1234567");
Ok(ChaCha20::new(&key, &nonce))
}
#[cfg(test)]
mod tests {
use super::super::secp;
use super::super::types;
use super::super::onion;
/// Test end-to-end Onion creation and unwrapping logic.
#[test]
fn onion() {
let value : u64 = 1000;
let blind = secp::insecure_rand_secret().unwrap();
let commitment = secp::commit(value, &blind).unwrap();
let session_key = secp::insecure_rand_secret().unwrap();
let mut hops : Vec<types::Hop> = Vec::new();
let mut keys : Vec<secp::SecretKey> = Vec::new();
let mut final_commit = commitment.clone();
let mut final_blind = blind.clone();
for i in 0..5 {
keys.push(secp::insecure_rand_secret().unwrap());
let excess = secp::insecure_rand_secret().unwrap();
let secp = secp256k1zkp::Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit);
final_blind.add_assign(&secp, &excess).unwrap();
final_commit = secp::add_excess(&final_commit, &excess).unwrap();
let proof = if i == 4 {
let n1 = secp::insecure_rand_secret().unwrap();
let rp = secp.bullet_proof(value, final_blind.clone(), n1.clone(), n1.clone(), None, None);
assert!(secp.verify_bullet_proof(final_commit, rp, None).is_ok());
Some(rp)
} else {
None
};
hops.push(types::Hop{
pubkey: secp::PublicKey::from_secret_key(&secp, &keys[i]).unwrap(),
payload: types::Payload{
excess: excess,
rangeproof: proof,
}
});
}
let mut onion_packet = onion::create_onion(&commitment, &session_key, &hops).unwrap();
let mut payload = types::Payload{
excess: secp::insecure_rand_secret().unwrap(),
rangeproof: None
};
for i in 0..5 {
let peeled = onion::peel_layer(&onion_packet, &keys[i]).unwrap();
payload = peeled.0;
onion_packet = peeled.1;
}
assert!(payload.rangeproof.is_some());
assert_eq!(payload.rangeproof.unwrap(), hops[4].payload.rangeproof.unwrap());
assert_eq!(secp::commit(value, &final_blind).unwrap(), final_commit);
}
}

194
src/secp.rs Normal file
View file

@ -0,0 +1,194 @@
pub use secp256k1zkp::{ContextFlag, Message, Secp256k1, Signature};
pub use secp256k1zkp::ecdh::SharedSecret;
pub use secp256k1zkp::pedersen::{Commitment, RangeProof};
pub use secp256k1zkp::key::{PublicKey, SecretKey};
pub use secp256k1zkp::constants::{AGG_SIGNATURE_SIZE, COMPRESSED_PUBLIC_KEY_SIZE, MAX_PROOF_SIZE, PEDERSEN_COMMITMENT_SIZE, SECRET_KEY_SIZE};
use crate::ser::{Readable, Reader, Writeable, Writer};
use crate::error::{ErrorKind, Result};
use rand::RngCore;
use std::cmp;
/// A generalized Schnorr signature with a pedersen commitment value & blinding factors as the keys
pub const COM_SIGNATURE_SIZE : usize = 96;
pub struct ComSignature(pub [u8; COM_SIGNATURE_SIZE]);
impl ComSignature {
/// Builds a ComSignature from a byte vector. If the vector is too short, it will be
/// completed by zeroes. If it's too long, it will be truncated.
pub fn from_vec(v: Vec<u8>) -> ComSignature {
let mut h = [0; COM_SIGNATURE_SIZE];
for i in 0..cmp::min(v.len(), COM_SIGNATURE_SIZE) {
h[i] = v[i];
}
ComSignature(h)
}
#[allow(dead_code)]
pub fn sign(_value: u64, _blind: &SecretKey, _msg: &Vec<u8>) -> Result<ComSignature> {
// milestone 2 - todo
let mut h = [0u8; COM_SIGNATURE_SIZE];
for i in 0..COM_SIGNATURE_SIZE {
h[i] = i as u8;
}
Ok(ComSignature(h))
}
pub fn verify(self, _commit: &Commitment, _msg: &Vec<u8>) -> Result<()> {
// milestone 2 - todo
Ok(())
}
}
impl AsRef<[u8]> for ComSignature {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
/// Serializes a ComSignature to and from hex
pub mod comsig_serde {
use super::ComSignature;
use serde::{Deserialize, Serializer};
use grin_util::ToHex;
/// Serializes a ComSignature as a hex string
pub fn serialize<S>(comsig: &ComSignature, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&comsig.to_hex())
}
/// Creates a ComSignature from a hex string
pub fn deserialize<'de, D>(deserializer: D) -> std::result::Result<ComSignature, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
String::deserialize(deserializer)
.and_then(|string| grin_util::from_hex(&string).map_err(Error::custom))
.and_then(|bytes: Vec<u8>| Ok(ComSignature::from_vec(bytes.to_vec())))
}
}
/// Compute a PublicKey from a SecretKey
pub fn to_public_key(secret_key: &SecretKey) -> Result<PublicKey> {
let secp = Secp256k1::new();
let pubkey = PublicKey::from_secret_key(&secp, secret_key)?;
Ok(pubkey)
}
/// Generate a random SecretKey. Not for production use
pub fn insecure_rand_secret() -> Result<SecretKey> {
let secp = Secp256k1::new();
let mut seed = [0u8; 32];
rand::thread_rng().fill_bytes(&mut seed);
let secret = SecretKey::from_slice(&secp, &seed)?;
Ok(secret)
}
/// Build a Pedersen Commitment using the provided value and blinding factor
pub fn commit(value: u64, blind: &SecretKey) -> Result<Commitment> {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let commit = secp.commit(value, blind.clone())?;
Ok(commit)
}
/// Add a blinding factor to an existing Commitment
pub fn add_excess(commitment: &Commitment, excess: &SecretKey) -> Result<Commitment> {
let secp = Secp256k1::with_caps(ContextFlag::Commit);
let excess_commit : Commitment = secp.commit(0, excess.clone())?;
let commits = vec![commitment.clone(), excess_commit.clone()];
let sum = secp.commit_sum(commits, Vec::new())?;
Ok(sum)
}
/// secp256k1-zkp object serialization
impl Readable for Commitment {
fn read<R: Reader>(reader: &mut R) -> Result<Commitment> {
let a = reader.read_fixed_bytes(PEDERSEN_COMMITMENT_SIZE)?;
let mut c = [0; PEDERSEN_COMMITMENT_SIZE];
c[..PEDERSEN_COMMITMENT_SIZE].clone_from_slice(&a[..PEDERSEN_COMMITMENT_SIZE]);
Ok(Commitment(c))
}
}
impl Writeable for Commitment {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
writer.write_fixed_bytes(self)
}
}
impl Readable for RangeProof {
fn read<R: Reader>(reader: &mut R) -> Result<RangeProof> {
let len = reader.read_u64()?;
let max_len = cmp::min(len as usize, MAX_PROOF_SIZE);
let p = reader.read_fixed_bytes(max_len)?;
let mut proof = [0; MAX_PROOF_SIZE];
proof[..p.len()].clone_from_slice(&p[..]);
Ok(RangeProof {
plen: proof.len(),
proof,
})
}
}
impl Writeable for RangeProof {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
writer.write_bytes(self)
}
}
impl Readable for Signature {
fn read<R: Reader>(reader: &mut R) -> Result<Signature> {
let a = reader.read_fixed_bytes(AGG_SIGNATURE_SIZE)?;
let mut c = [0; AGG_SIGNATURE_SIZE];
c[..AGG_SIGNATURE_SIZE].clone_from_slice(&a[..AGG_SIGNATURE_SIZE]);
Ok(Signature::from_raw_data(&c).unwrap())
}
}
impl Writeable for Signature {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
writer.write_fixed_bytes(self)
}
}
impl Readable for PublicKey {
// Read the public key in compressed form
fn read<R: Reader>(reader: &mut R) -> Result<Self> {
let buf = reader.read_fixed_bytes(COMPRESSED_PUBLIC_KEY_SIZE)?;
let secp = Secp256k1::with_caps(ContextFlag::None);
let pk = PublicKey::from_slice(&secp, &buf).map_err(|_| ErrorKind::CorruptedData)?;
Ok(pk)
}
}
impl Writeable for PublicKey {
// Write the public key in compressed form
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
let secp = Secp256k1::with_caps(ContextFlag::None);
writer.write_fixed_bytes(self.serialize_vec(&secp, true))?;
Ok(())
}
}
impl Readable for SecretKey {
fn read<R: Reader>(reader: &mut R) -> Result<Self> {
let buf = reader.read_fixed_bytes(SECRET_KEY_SIZE)?;
let secp = Secp256k1::with_caps(ContextFlag::None);
let pk = SecretKey::from_slice(&secp, &buf).map_err(|_| ErrorKind::CorruptedData)?;
Ok(pk)
}
}
impl Writeable for SecretKey {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
writer.write_fixed_bytes(self.0)?;
Ok(())
}
}

624
src/ser.rs Normal file
View file

@ -0,0 +1,624 @@
// Copyright 2021 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Serialization and deserialization layer specialized for binary encoding.
//! Ensures consistency and safety. Basically a minimal subset or
//! rustc_serialize customized for our need.
//!
//! To use it simply implement `Writeable` or `Readable` and then use the
//! `serialize` or `deserialize` functions on them as appropriate.
use crate::error::{Error, ErrorKind, Result};
use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
use bytes::Buf;
use std::io::{self, Read, Write};
use std::marker;
/// Implementations defined how different numbers and binary structures are
/// written to an underlying stream or container (depending on implementation).
pub trait Writer {
/// Writes a u8 as bytes
fn write_u8(&mut self, n: u8) -> Result<()> {
self.write_fixed_bytes(&[n])
}
/// Writes a u16 as bytes
fn write_u16(&mut self, n: u16) -> Result<()> {
let mut bytes = [0; 2];
BigEndian::write_u16(&mut bytes, n);
self.write_fixed_bytes(&bytes)
}
/// Writes a u32 as bytes
fn write_u32(&mut self, n: u32) -> Result<()> {
let mut bytes = [0; 4];
BigEndian::write_u32(&mut bytes, n);
self.write_fixed_bytes(&bytes)
}
/// Writes a u32 as bytes
fn write_i32(&mut self, n: i32) -> Result<()> {
let mut bytes = [0; 4];
BigEndian::write_i32(&mut bytes, n);
self.write_fixed_bytes(&bytes)
}
/// Writes a u64 as bytes
fn write_u64(&mut self, n: u64) -> Result<()> {
let mut bytes = [0; 8];
BigEndian::write_u64(&mut bytes, n);
self.write_fixed_bytes(&bytes)
}
/// Writes a i64 as bytes
fn write_i64(&mut self, n: i64) -> Result<()> {
let mut bytes = [0; 8];
BigEndian::write_i64(&mut bytes, n);
self.write_fixed_bytes(&bytes)
}
/// Writes a variable number of bytes. The length is encoded as a 64-bit
/// prefix.
fn write_bytes<T: AsRef<[u8]>>(&mut self, bytes: T) -> Result<()> {
self.write_u64(bytes.as_ref().len() as u64)?;
self.write_fixed_bytes(bytes)
}
/// Writes a fixed number of bytes. The reader is expected to know the actual length on read.
fn write_fixed_bytes<T: AsRef<[u8]>>(&mut self, bytes: T) -> Result<()>;
/// Writes a fixed length of "empty" bytes.
fn write_empty_bytes(&mut self, length: usize) -> Result<()> {
self.write_fixed_bytes(vec![0u8; length])
}
}
/// Implementations defined how different numbers and binary structures are
/// read from an underlying stream or container (depending on implementation).
pub trait Reader {
/// Read a u8 from the underlying Read
fn read_u8(&mut self) -> Result<u8>;
/// Read a u16 from the underlying Read
fn read_u16(&mut self) -> Result<u16>;
/// Read a u32 from the underlying Read
fn read_u32(&mut self) -> Result<u32>;
/// Read a u64 from the underlying Read
fn read_u64(&mut self) -> Result<u64>;
/// Read a i32 from the underlying Read
fn read_i32(&mut self) -> Result<i32>;
/// Read a i64 from the underlying Read
fn read_i64(&mut self) -> Result<i64>;
/// Read a u64 len prefix followed by that number of exact bytes.
fn read_bytes_len_prefix(&mut self) -> Result<Vec<u8>>;
/// Read a fixed number of bytes from the underlying reader.
fn read_fixed_bytes(&mut self, length: usize) -> Result<Vec<u8>>;
/// Consumes a byte from the reader, producing an error if it doesn't have
/// the expected value
fn expect_u8(&mut self, val: u8) -> Result<u8>;
/// Read a fixed number of "empty" bytes from the underlying reader.
/// It is an error if any non-empty bytes encountered.
fn read_empty_bytes(&mut self, length: usize) -> Result<()> {
for _ in 0..length {
if self.read_u8()? != 0u8 {
return Err(ErrorKind::CorruptedData.into());
}
}
Ok(())
}
}
/// Trait that every type that can be serialized as binary must implement.
/// Writes directly to a Writer, a utility type thinly wrapping an
/// underlying Write implementation.
pub trait Writeable {
/// Write the data held by this Writeable to the provided writer
fn write<W: Writer>(&self, writer: &mut W) -> Result<()>;
}
/// Reader that exposes an Iterator interface.
pub struct IteratingReader<'a, T, R: Reader> {
count: u64,
curr: u64,
reader: &'a mut R,
_marker: marker::PhantomData<T>,
}
impl<'a, T, R: Reader> IteratingReader<'a, T, R> {
/// Constructor to create a new iterating reader for the provided underlying reader.
/// Takes a count so we know how many to iterate over.
pub fn new(reader: &'a mut R, count: u64) -> Self {
let curr = 0;
IteratingReader {
count,
curr,
reader,
_marker: marker::PhantomData,
}
}
}
impl<'a, T, R> Iterator for IteratingReader<'a, T, R>
where
T: Readable,
R: Reader,
{
type Item = T;
fn next(&mut self) -> Option<T> {
if self.curr >= self.count {
return None;
}
self.curr += 1;
T::read(self.reader).ok()
}
}
/// Reads multiple serialized items into a Vec.
pub fn read_multi<T, R>(reader: &mut R, count: u64) -> Result<Vec<T>>
where
T: Readable,
R: Reader,
{
// Very rudimentary check to ensure we do not overflow anything
// attempting to read huge amounts of data.
// Probably better than checking if count * size overflows a u64 though.
if count > 1_000_000 {
return Err(ErrorKind::TooLargeReadErr.into());
}
let res: Vec<T> = IteratingReader::new(reader, count).collect();
if res.len() as u64 != count {
return Err(ErrorKind::CountError.into());
}
Ok(res)
}
/// Trait that every type that can be deserialized from binary must implement.
/// Reads directly to a Reader, a utility type thinly wrapping an
/// underlying Read implementation.
pub trait Readable
where
Self: Sized,
{
/// Reads the data necessary to this Readable from the provided reader
fn read<R: Reader>(reader: &mut R) -> Result<Self>;
}
/// Deserializes a Readable from any std::io::Read implementation.
pub fn deserialize<T: Readable, R: Read>(source: &mut R) -> Result<T> {
let mut reader = BinReader::new(source);
T::read(&mut reader)
}
/// Serializes a Writeable into any std::io::Write implementation.
pub fn serialize<W: Writeable>(sink: &mut dyn Write, thing: &W) -> Result<()> {
let mut writer = BinWriter::new(sink);
thing.write(&mut writer)
}
/// Utility function to serialize a writeable directly in memory using a
/// Vec<u8>.
pub fn ser_vec<W: Writeable>(thing: &W) -> Result<Vec<u8>> {
let mut vec = vec![];
serialize(&mut vec, thing)?;
Ok(vec)
}
/// Utility to read from a binary source
pub struct BinReader<'a, R: Read> {
source: &'a mut R,
}
impl<'a, R: Read> BinReader<'a, R> {
/// Constructor for a new BinReader for the provided source.
pub fn new(source: &'a mut R) -> Self {
BinReader { source }
}
}
fn map_io_err(err: io::Error) -> Error {
ErrorKind::IOErr(format!("{}", err), err.kind()).into()
}
/// Utility wrapper for an underlying byte Reader. Defines higher level methods
/// to read numbers, byte vectors, hashes, etc.
impl<'a, R: Read> Reader for BinReader<'a, R> {
fn read_u8(&mut self) -> Result<u8> {
self.source.read_u8().map_err(map_io_err)
}
fn read_u16(&mut self) -> Result<u16> {
self.source.read_u16::<BigEndian>().map_err(map_io_err)
}
fn read_u32(&mut self) -> Result<u32> {
self.source.read_u32::<BigEndian>().map_err(map_io_err)
}
fn read_i32(&mut self) -> Result<i32> {
self.source.read_i32::<BigEndian>().map_err(map_io_err)
}
fn read_u64(&mut self) -> Result<u64> {
self.source.read_u64::<BigEndian>().map_err(map_io_err)
}
fn read_i64(&mut self) -> Result<i64> {
self.source.read_i64::<BigEndian>().map_err(map_io_err)
}
/// Read a variable size vector from the underlying Read. Expects a usize
fn read_bytes_len_prefix(&mut self) -> Result<Vec<u8>> {
let len = self.read_u64()?;
self.read_fixed_bytes(len as usize)
}
/// Read a fixed number of bytes.
fn read_fixed_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
// not reading more than 100k bytes in a single read
if len > 100_000 {
return Err(ErrorKind::TooLargeReadErr.into());
}
let mut buf = vec![0; len];
self.source
.read_exact(&mut buf)
.map(move |_| buf)
.map_err(map_io_err)
}
fn expect_u8(&mut self, val: u8) -> Result<u8> {
let b = self.read_u8()?;
if b == val {
Ok(b)
} else {
Err(ErrorKind::UnexpectedData {
expected: vec![val],
received: vec![b],
}.into())
}
}
}
/// A reader that reads straight off a stream.
/// Tracks total bytes read so we can verify we read the right number afterwards.
pub struct StreamingReader<'a> {
total_bytes_read: u64,
stream: &'a mut dyn Read,
}
impl<'a> StreamingReader<'a> {
/// Create a new streaming reader with the provided underlying stream.
/// Also takes a duration to be used for each individual read_exact call.
pub fn new(stream: &'a mut dyn Read) -> StreamingReader<'a> {
StreamingReader {
total_bytes_read: 0,
stream,
}
}
/// Returns the total bytes read via this streaming reader.
pub fn total_bytes_read(&self) -> u64 {
self.total_bytes_read
}
}
/// Note: We use read_fixed_bytes() here to ensure our "async" I/O behaves as expected.
impl<'a> Reader for StreamingReader<'a> {
fn read_u8(&mut self) -> Result<u8> {
let buf = self.read_fixed_bytes(1)?;
Ok(buf[0])
}
fn read_u16(&mut self) -> Result<u16> {
let buf = self.read_fixed_bytes(2)?;
Ok(BigEndian::read_u16(&buf[..]))
}
fn read_u32(&mut self) -> Result<u32> {
let buf = self.read_fixed_bytes(4)?;
Ok(BigEndian::read_u32(&buf[..]))
}
fn read_i32(&mut self) -> Result<i32> {
let buf = self.read_fixed_bytes(4)?;
Ok(BigEndian::read_i32(&buf[..]))
}
fn read_u64(&mut self) -> Result<u64> {
let buf = self.read_fixed_bytes(8)?;
Ok(BigEndian::read_u64(&buf[..]))
}
fn read_i64(&mut self) -> Result<i64> {
let buf = self.read_fixed_bytes(8)?;
Ok(BigEndian::read_i64(&buf[..]))
}
/// Read a variable size vector from the underlying stream. Expects a usize
fn read_bytes_len_prefix(&mut self) -> Result<Vec<u8>> {
let len = self.read_u64()?;
self.total_bytes_read += 8;
self.read_fixed_bytes(len as usize)
}
/// Read a fixed number of bytes.
fn read_fixed_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
let mut buf = vec![0u8; len];
self.stream.read_exact(&mut buf)?;
self.total_bytes_read += len as u64;
Ok(buf)
}
fn expect_u8(&mut self, val: u8) -> Result<u8> {
let b = self.read_u8()?;
if b == val {
Ok(b)
} else {
Err(ErrorKind::UnexpectedData {
expected: vec![val],
received: vec![b],
}.into())
}
}
}
/// Protocol version-aware wrapper around a `Buf` impl
pub struct BufReader<'a, B: Buf> {
inner: &'a mut B,
bytes_read: usize,
}
impl<'a, B: Buf> BufReader<'a, B> {
/// Construct a new BufReader
pub fn new(buf: &'a mut B) -> Self {
Self {
inner: buf,
bytes_read: 0,
}
}
/// Check whether the buffer has enough bytes remaining to perform a read
fn has_remaining(&mut self, len: usize) -> Result<()> {
if self.inner.remaining() >= len {
self.bytes_read += len;
Ok(())
} else {
Err(io::ErrorKind::UnexpectedEof.into())
}
}
/// The total bytes read
pub fn bytes_read(&self) -> u64 {
self.bytes_read as u64
}
/// Convenience function to read from the buffer and deserialize
pub fn body<T: Readable>(&mut self) -> Result<T> {
T::read(self)
}
}
impl<'a, B: Buf> Reader for BufReader<'a, B> {
fn read_u8(&mut self) -> Result<u8> {
self.has_remaining(1)?;
Ok(self.inner.get_u8())
}
fn read_u16(&mut self) -> Result<u16> {
self.has_remaining(2)?;
Ok(self.inner.get_u16())
}
fn read_u32(&mut self) -> Result<u32> {
self.has_remaining(4)?;
Ok(self.inner.get_u32())
}
fn read_u64(&mut self) -> Result<u64> {
self.has_remaining(8)?;
Ok(self.inner.get_u64())
}
fn read_i32(&mut self) -> Result<i32> {
self.has_remaining(4)?;
Ok(self.inner.get_i32())
}
fn read_i64(&mut self) -> Result<i64> {
self.has_remaining(8)?;
Ok(self.inner.get_i64())
}
fn read_bytes_len_prefix(&mut self) -> Result<Vec<u8>> {
let len = self.read_u64()?;
self.read_fixed_bytes(len as usize)
}
fn read_fixed_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
// not reading more than 100k bytes in a single read
if len > 100_000 {
return Err(ErrorKind::TooLargeReadErr.into());
}
self.has_remaining(len)?;
let mut buf = vec![0; len];
self.inner.copy_to_slice(&mut buf[..]);
Ok(buf)
}
fn expect_u8(&mut self, val: u8) -> Result<u8> {
let b = self.read_u8()?;
if b == val {
Ok(b)
} else {
Err(ErrorKind::UnexpectedData {
expected: vec![val],
received: vec![b],
}.into())
}
}
}
/// Utility wrapper for an underlying byte Writer. Defines higher level methods
/// to write numbers, byte vectors, hashes, etc.
pub struct BinWriter<'a> {
sink: &'a mut dyn Write,
}
impl<'a> BinWriter<'a> {
/// Wraps a standard Write in a new BinWriter
pub fn new(sink: &'a mut dyn Write) -> BinWriter<'a> {
BinWriter { sink }
}
}
impl<'a> Writer for BinWriter<'a> {
fn write_fixed_bytes<T: AsRef<[u8]>>(&mut self, bytes: T) -> Result<()> {
self.sink.write_all(bytes.as_ref())?;
Ok(())
}
}
macro_rules! impl_int {
($int:ty, $w_fn:ident, $r_fn:ident) => {
impl Writeable for $int {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
writer.$w_fn(*self)
}
}
impl Readable for $int {
fn read<R: Reader>(reader: &mut R) -> Result<$int> {
reader.$r_fn()
}
}
};
}
impl_int!(u8, write_u8, read_u8);
impl_int!(u16, write_u16, read_u16);
impl_int!(u32, write_u32, read_u32);
impl_int!(i32, write_i32, read_i32);
impl_int!(u64, write_u64, read_u64);
impl_int!(i64, write_i64, read_i64);
impl<T> Readable for Vec<T>
where
T: Readable,
{
fn read<R: Reader>(reader: &mut R) -> Result<Vec<T>> {
let mut buf = Vec::new();
loop {
let elem = T::read(reader);
match elem {
Ok(e) => buf.push(e),
// Err(ErrorKind::IOErr(ref _d, ref kind)) if *kind == io::ErrorKind::UnexpectedEof => {
// break;
// }
Err(e) => {
match e.kind() {
ErrorKind::IOErr(ref _d, ref kind) if *kind == io::ErrorKind::UnexpectedEof => {
break;
},
_ => return Err(e),
}
},
}
}
Ok(buf)
}
}
impl<T> Writeable for Vec<T>
where
T: Writeable,
{
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
for elmt in self {
elmt.write(writer)?;
}
Ok(())
}
}
impl<'a, A: Writeable> Writeable for &'a A {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
Writeable::write(*self, writer)
}
}
impl<A: Writeable, B: Writeable> Writeable for (A, B) {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
Writeable::write(&self.0, writer)?;
Writeable::write(&self.1, writer)
}
}
impl<A: Readable, B: Readable> Readable for (A, B) {
fn read<R: Reader>(reader: &mut R) -> Result<(A, B)> {
Ok((Readable::read(reader)?, Readable::read(reader)?))
}
}
impl<A: Writeable, B: Writeable, C: Writeable> Writeable for (A, B, C) {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
Writeable::write(&self.0, writer)?;
Writeable::write(&self.1, writer)?;
Writeable::write(&self.2, writer)
}
}
impl<A: Writeable, B: Writeable, C: Writeable, D: Writeable> Writeable for (A, B, C, D) {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
Writeable::write(&self.0, writer)?;
Writeable::write(&self.1, writer)?;
Writeable::write(&self.2, writer)?;
Writeable::write(&self.3, writer)
}
}
impl<A: Readable, B: Readable, C: Readable> Readable for (A, B, C) {
fn read<R: Reader>(reader: &mut R) -> Result<(A, B, C)> {
Ok((
Readable::read(reader)?,
Readable::read(reader)?,
Readable::read(reader)?,
))
}
}
impl<A: Readable, B: Readable, C: Readable, D: Readable> Readable for (A, B, C, D) {
fn read<R: Reader>(reader: &mut R) -> Result<(A, B, C, D)> {
Ok((
Readable::read(reader)?,
Readable::read(reader)?,
Readable::read(reader)?,
Readable::read(reader)?,
))
}
}
/// Serializes a Vec<u8> to and from hex
pub mod vec_serde {
use serde::{Deserialize, Serializer};
use grin_util::ToHex;
/// Serializes a Vec<u8> as a hex string
pub fn serialize<S>(bytes: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&bytes.to_hex())
}
/// Creates a Vec<u8> from a hex string
pub fn deserialize<'de, D>(deserializer: D) -> std::result::Result<Vec<u8>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
String::deserialize(deserializer)
.and_then(|string| grin_util::from_hex(&string).map_err(Error::custom))
}
}

211
src/server.rs Normal file
View file

@ -0,0 +1,211 @@
use crate::onion;
use crate::secp::{self, Commitment, ComSignature, SecretKey};
use crate::ser;
use crate::types::Onion;
use jsonrpc_derive::rpc;
use jsonrpc_http_server::*;
use jsonrpc_http_server::jsonrpc_core::*;
use jsonrpc_core::{Result, Value};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::sync::Mutex;
#[derive(Clone, Debug, PartialEq)]
pub struct ServerConfig {
pub key: SecretKey,
pub addr: SocketAddr,
pub is_first: bool,
}
pub struct Submission {
pub excess: SecretKey,
pub input_commit: Commitment,
pub onion: Onion,
}
#[derive(Serialize, Deserialize)]
pub struct SwapReq {
pub onion: Onion,
#[serde(with = "ser::vec_serde")]
pub msg: Vec<u8>,
#[serde(with = "secp::comsig_serde")]
pub comsig: ComSignature,
}
lazy_static! {
static ref SERVER_STATE: Mutex<Vec<Submission>> = Mutex::new(Vec::new());
}
#[rpc(server)]
pub trait Server {
#[rpc(name = "swap")]
fn swap(&self, swap: SwapReq) -> Result<Value>;
// milestone 3:
// fn derive_outputs(&self, entries: Vec<Onion>) -> Result<Value>;
// fn derive_kernel(&self, tx: Tx) -> Result<Value>;
}
pub struct ServerImpl {
server_key: SecretKey,
}
impl ServerImpl {
pub fn new(server_key: SecretKey) -> Self {
ServerImpl { server_key }
}
}
impl Server for ServerImpl {
fn swap(&self, swap: SwapReq) -> Result<Value> {
// milestone 2 - check that commitment is unspent
// Verify commitment signature to ensure caller owns the output
let _ = swap.comsig.verify(&swap.onion.commit, &swap.msg)
.map_err(|_| jsonrpc_core::Error::invalid_params("ComSignature invalid"))?;
let peeled = onion::peel_layer(&swap.onion, &self.server_key)
.map_err(|e| jsonrpc_core::Error::invalid_params(e.message()))?;
SERVER_STATE.lock().unwrap().push(Submission{
excess: peeled.0.excess,
input_commit: swap.onion.commit,
onion: peeled.1
});
Ok(Value::String("success".into()))
}
}
/// Spin up the JSON-RPC web server
pub fn listen<F>(server_config: &ServerConfig, shutdown_signal: F) -> std::result::Result<(), Box<dyn std::error::Error>>
where
F: futures::future::Future<Output = ()> + Send + 'static,
{
let mut io = IoHandler::new();
io.extend_with(ServerImpl::to_delegate(ServerImpl::new(server_config.key.clone())));
let server = ServerBuilder::new(io)
.cors(DomainsValidation::Disabled)
.request_middleware(|request: hyper::Request<hyper::Body>| {
if request.uri() == "/v1" {
request.into()
} else {
jsonrpc_http_server::Response::bad_request("Only v1 supported").into()
}
})
.start_http(&server_config.addr)
.expect("Unable to start RPC server");
let close_handle = server.close_handle();
std::thread::spawn(move || {
futures::executor::block_on(shutdown_signal);
close_handle.close();
});
server.wait();
Ok(())
}
#[cfg(test)]
mod tests {
use crate::{onion, secp, server, types};
use std::net::TcpListener;
use std::time::Duration;
use std::thread;
use hyper::{Body, Client, Request, Response};
use tokio::runtime;
async fn body_to_string(req: Response<Body>) -> String {
let body_bytes = hyper::body::to_bytes(req.into_body()).await.unwrap();
String::from_utf8(body_bytes.to_vec()).unwrap()
}
/// Spin up a temporary web service, query the API, then cleanup and return response
fn make_request(server_key: secp::SecretKey, req: String) -> Result<String, Box<dyn std::error::Error>> {
let server_config = server::ServerConfig {
key: server_key,
addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?,
is_first: true
};
let threaded_rt = runtime::Runtime::new()?;
let (shutdown_sender, shutdown_receiver) = futures::channel::oneshot::channel();
let uri = format!("http://{}/v1", server_config.addr);
// Spawn the server task
threaded_rt.spawn(async move {
server::listen(&server_config, async { shutdown_receiver.await.ok(); }).unwrap()
});
// Wait for listener
thread::sleep(Duration::from_millis(500));
let do_request = async move {
let request = Request::post(uri)
.header("Content-Type", "application/json")
.body(Body::from(req))
.unwrap();
Client::new().request(request).await
};
let response = threaded_rt.block_on(do_request)?;
let response_str: String = threaded_rt.block_on(body_to_string(response));
shutdown_sender.send(()).ok();
// Wait for shutdown
thread::sleep(Duration::from_millis(500));
threaded_rt.shutdown_background();
Ok(response_str)
}
#[test]
fn swap_lifecycle() -> Result<(), Box<dyn std::error::Error>> {
let server_key = secp::insecure_rand_secret()?;
let secp = secp::Secp256k1::new();
let value: u64 = 100;
let blind = secp::insecure_rand_secret()?;
let commitment = secp::commit(value, &blind)?;
let session_key = secp::insecure_rand_secret()?;
let hop = types::Hop {
pubkey: secp::PublicKey::from_secret_key(&secp, &server_key)?,
payload: types::Payload{
excess: secp::insecure_rand_secret()?,
rangeproof: None,
}
};
let hops: Vec<types::Hop> = vec![hop];
let onion_packet = onion::create_onion(&commitment, &session_key, &hops)?;
let msg : Vec<u8> = vec![0u8, 1u8, 2u8, 3u8];
let comsig = secp::ComSignature::sign(value, &blind, &msg)?;
let swap = server::SwapReq{
onion: onion_packet,
msg: msg,
comsig: comsig,
};
let req = format!("{{\"jsonrpc\": \"2.0\", \"method\": \"swap\", \"params\": [{}], \"id\": \"1\"}}", serde_json::json!(swap));
let response = make_request(server_key, req)?;
let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"success\",\"id\":\"1\"}\n";
assert_eq!(response, expected);
Ok(())
}
#[test]
fn swap_bad_request() -> Result<(), Box<dyn std::error::Error>> {
let params = "{ \"param\": \"Not a valid Swap request\" }";
let req = format!("{{\"jsonrpc\": \"2.0\", \"method\": \"swap\", \"params\": [{}], \"id\": \"1\"}}", params);
let response = make_request(secp::insecure_rand_secret()?, req)?;
let expected = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: missing field `onion`.\"},\"id\":\"1\"}\n";
assert_eq!(response, expected);
Ok(())
}
// milestone 2 - add tests to cover invalid comsig's & inputs not in utxo set
}

166
src/types.rs Normal file
View file

@ -0,0 +1,166 @@
use crate::error::{ErrorKind, Result};
use crate::secp::{Commitment, PublicKey, RangeProof, SecretKey, Secp256k1};
use crate::ser::{self, BinReader, Readable, Reader, Writeable, Writer};
use grin_util::{self, ToHex};
use serde::{Deserialize, Serialize};
use serde::ser::SerializeStruct;
use std::fmt;
use std::io::Cursor;
pub type RawBytes = Vec<u8>;
const CURRENT_VERSION : u8 = 0;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Payload {
pub excess: SecretKey,
pub rangeproof: Option<RangeProof>,
}
impl Readable for Payload {
fn read<R: Reader>(reader: &mut R) -> Result<Payload> {
let version = reader.read_u8()?;
if version != CURRENT_VERSION {
return Err(ErrorKind::UnsupportedPayload.into());
}
let excess = SecretKey::read(reader)?;
let rangeproof = if reader.read_u8()? == 0 {
None
} else {
Some(RangeProof::read(reader)?)
};
let payload = Payload {
excess: excess,
rangeproof: rangeproof
};
Ok(payload)
}
}
impl Writeable for Payload {
fn write<W: Writer>(&self, writer: &mut W) -> Result<()> {
writer.write_u8(CURRENT_VERSION)?;
writer.write_fixed_bytes(&self.excess)?;
match &self.rangeproof {
Some(proof) => {
writer.write_u8(1)?;
proof.write(writer)?;
},
None => writer.write_u8(0)?,
};
Ok(())
}
}
pub fn serialize_payload(payload: &Payload) -> Result<Vec<u8>> {
ser::ser_vec(&payload)
}
pub fn deserialize_payload(bytes: &Vec<u8>) -> Result<Payload> {
let mut cursor = Cursor::new(&bytes);
let mut reader = BinReader::new(&mut cursor);
Payload::read(&mut reader)
}
pub struct Hop {
pub pubkey: PublicKey,
pub payload: Payload,
}
pub struct Onion {
pub ephemeral_pubkey: PublicKey,
pub commit: Commitment,
pub enc_payloads: Vec<RawBytes>,
}
impl serde::ser::Serialize for Onion {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
let mut state = serializer.serialize_struct("Onion", 3)?;
let secp = Secp256k1::new();
state.serialize_field("pubkey", &self.ephemeral_pubkey.serialize_vec(&secp, true).to_hex())?;
state.serialize_field("commit", &self.commit.to_hex())?;
let hex_payloads: Vec<String> = self.enc_payloads.iter().map(|v| v.to_hex()).collect();
state.serialize_field("data", &hex_payloads)?;
state.end()
}
}
impl<'de> serde::de::Deserialize<'de> for Onion {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "snake_case")]
enum Field {
Pubkey,
Commit,
Data
}
struct OnionVisitor;
impl<'de> serde::de::Visitor<'de> for OnionVisitor {
type Value = Onion;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an Onion")
}
fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
let mut pubkey = None;
let mut commit = None;
let mut data = None;
while let Some(key) = map.next_key()? {
match key {
Field::Pubkey => {
let val: String = map.next_value()?;
let vec = grin_util::from_hex(&val).map_err(serde::de::Error::custom)?;
let secp = Secp256k1::new();
pubkey = Some(PublicKey::from_slice(&secp, &vec[..]).map_err(serde::de::Error::custom)?);
}
Field::Commit => {
let val: String = map.next_value()?;
let vec = grin_util::from_hex(&val).map_err(serde::de::Error::custom)?;
commit = Some(Commitment::from_vec(vec));
}
Field::Data => {
let val: Vec<String> = map.next_value()?;
let mut vec: Vec<Vec<u8>> = Vec::new();
for hex in val {
vec.push(grin_util::from_hex(&hex).map_err(serde::de::Error::custom)?);
}
data = Some(vec);
}
}
}
Ok(Onion {
ephemeral_pubkey: pubkey.unwrap(),
commit: commit.unwrap(),
enc_payloads: data.unwrap(),
})
}
}
const FIELDS: &[&str] = &[
"pubkey",
"commit",
"data"
];
deserializer.deserialize_struct("Onion", &FIELDS, OnionVisitor)
}
}