mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-01 17:01:09 +03:00
* Fix #566: Use serdes to serialize commit to hex * Add printable UTXO
This commit is contained in:
parent
86ff4e5bd0
commit
d754b8d1e4
3 changed files with 210 additions and 34 deletions
231
api/src/types.rs
231
api/src/types.rs
|
@ -17,11 +17,25 @@ use std::sync::Arc;
|
||||||
use core::{core, ser};
|
use core::{core, ser};
|
||||||
use core::core::hash::Hashed;
|
use core::core::hash::Hashed;
|
||||||
use core::core::SumCommit;
|
use core::core::SumCommit;
|
||||||
|
use core::core::SwitchCommitHash;
|
||||||
use chain;
|
use chain;
|
||||||
use p2p;
|
use p2p;
|
||||||
use util;
|
use util;
|
||||||
use util::secp::pedersen;
|
use util::secp::pedersen;
|
||||||
use util::secp::constants::MAX_PROOF_SIZE;
|
use util::secp::constants::MAX_PROOF_SIZE;
|
||||||
|
use serde;
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
|
use serde::de::MapAccess;
|
||||||
|
use std::fmt;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
macro_rules! no_dup {
|
||||||
|
($field: ident) => {
|
||||||
|
if $field.is_some() {
|
||||||
|
return Err(serde::de::Error::duplicate_field("$field"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// The state of the current fork tip
|
/// The state of the current fork tip
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
@ -153,29 +167,72 @@ pub enum OutputType {
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Utxo {
|
pub struct Utxo {
|
||||||
/// The output commitment representing the amount
|
/// The output commitment representing the amount
|
||||||
pub commit: pedersen::Commitment,
|
pub commit: PrintableCommitment,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Utxo {
|
impl Utxo {
|
||||||
pub fn new(commit: &pedersen::Commitment) -> Utxo {
|
pub fn new(commit: &pedersen::Commitment) -> Utxo {
|
||||||
Utxo { commit: commit.clone() }
|
Utxo { commit: PrintableCommitment(commit.clone()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PrintableCommitment(pedersen::Commitment);
|
||||||
|
|
||||||
|
impl PrintableCommitment {
|
||||||
|
pub fn commit(&self) -> pedersen::Commitment {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_vec(&self) -> Vec<u8> {
|
||||||
|
let commit = self.0;
|
||||||
|
commit.0.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::ser::Serialize for PrintableCommitment {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
|
||||||
|
S: serde::ser::Serializer {
|
||||||
|
serializer.serialize_str(&util::to_hex(self.to_vec()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::de::Deserialize<'de> for PrintableCommitment {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
|
||||||
|
D: serde::de::Deserializer<'de> {
|
||||||
|
deserializer.deserialize_str(PrintableCommitmentVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PrintableCommitmentVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for PrintableCommitmentVisitor {
|
||||||
|
type Value = PrintableCommitment;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a Pedersen commitment")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where
|
||||||
|
E: serde::de::Error, {
|
||||||
|
Ok(PrintableCommitment(pedersen::Commitment::from_vec(util::from_hex(String::from(v)).unwrap())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// As above, except formatted a bit better for human viewing
|
// As above, except formatted a bit better for human viewing
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct OutputPrintable {
|
pub struct OutputPrintable {
|
||||||
/// The type of output Coinbase|Transaction
|
/// The type of output Coinbase|Transaction
|
||||||
pub output_type: OutputType,
|
pub output_type: OutputType,
|
||||||
/// The homomorphic commitment representing the output's amount
|
/// The homomorphic commitment representing the output's amount
|
||||||
/// (as hex string)
|
/// (as hex string)
|
||||||
pub commit: String,
|
pub commit: pedersen::Commitment,
|
||||||
/// switch commit hash
|
/// switch commit hash
|
||||||
pub switch_commit_hash: String,
|
pub switch_commit_hash: SwitchCommitHash,
|
||||||
/// Whether the output has been spent
|
/// Whether the output has been spent
|
||||||
pub spent: bool,
|
pub spent: bool,
|
||||||
/// Rangeproof (as hex string)
|
/// Rangeproof (as hex string)
|
||||||
pub proof: Option<String>,
|
pub proof: Option<pedersen::RangeProof>,
|
||||||
/// Rangeproof hash (as hex string)
|
/// Rangeproof hash (as hex string)
|
||||||
pub proof_hash: String,
|
pub proof_hash: String,
|
||||||
}
|
}
|
||||||
|
@ -197,44 +254,143 @@ impl OutputPrintable {
|
||||||
let spent = chain.is_unspent(&out_id).is_err();
|
let spent = chain.is_unspent(&out_id).is_err();
|
||||||
|
|
||||||
let proof = if include_proof {
|
let proof = if include_proof {
|
||||||
Some(util::to_hex(output.proof.bytes().to_vec()))
|
Some(output.proof)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
OutputPrintable {
|
OutputPrintable {
|
||||||
output_type: output_type,
|
output_type,
|
||||||
commit: util::to_hex(output.commit.0.to_vec()),
|
commit: output.commit,
|
||||||
switch_commit_hash: output.switch_commit_hash.to_hex(),
|
switch_commit_hash: output.switch_commit_hash,
|
||||||
spent: spent,
|
spent,
|
||||||
proof: proof,
|
proof,
|
||||||
proof_hash: util::to_hex(output.proof.hash().to_vec()),
|
proof_hash: util::to_hex(output.proof.hash().to_vec()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the hex string back into a switch_commit_hash instance
|
// Convert the hex string back into a switch_commit_hash instance
|
||||||
pub fn switch_commit_hash(&self) -> Result<core::SwitchCommitHash, ser::Error> {
|
pub fn switch_commit_hash(&self) -> Result<core::SwitchCommitHash, ser::Error> {
|
||||||
core::SwitchCommitHash::from_hex(&self.switch_commit_hash)
|
Ok(self.switch_commit_hash.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn commit(&self) -> Result<pedersen::Commitment, ser::Error> {
|
pub fn commit(&self) -> Result<pedersen::Commitment, ser::Error> {
|
||||||
let vec = util::from_hex(self.commit.clone())
|
Ok(self.commit.clone())
|
||||||
.map_err(|_| ser::Error::HexError(format!("output commit hex_error")))?;
|
|
||||||
Ok(pedersen::Commitment::from_vec(vec))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range_proof(&self) -> Result<pedersen::RangeProof, ser::Error> {
|
pub fn range_proof(&self) -> Result<pedersen::RangeProof, ser::Error> {
|
||||||
if let Some(ref proof) = self.proof {
|
self.proof.clone().ok_or_else(|| ser::Error::HexError(format!("output range_proof missing")))
|
||||||
let vec = util::from_hex(proof.clone())
|
}
|
||||||
.map_err(|_| ser::Error::HexError(format!("output range_proof hex_error")))?;
|
}
|
||||||
let mut bytes = [0; MAX_PROOF_SIZE];
|
|
||||||
for i in 0..vec.len() {
|
impl serde::ser::Serialize for OutputPrintable {
|
||||||
bytes[i] = vec[i];
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
|
||||||
}
|
S: serde::ser::Serializer {
|
||||||
Ok(pedersen::RangeProof { proof: bytes, plen: vec.len() })
|
let mut state = serializer.serialize_struct("OutputPrintable", 6)?;
|
||||||
} else {
|
state.serialize_field("output_type", &self.output_type)?;
|
||||||
Err(ser::Error::HexError(format!("output range_proof missing")))
|
state.serialize_field("commit", &util::to_hex(self.commit.0.to_vec()))?;
|
||||||
|
state.serialize_field("switch_commit_hash", &self.switch_commit_hash.to_hex())?;
|
||||||
|
state.serialize_field("spent", &self.spent)?;
|
||||||
|
state.serialize_field("proof", &self.proof)?;
|
||||||
|
state.serialize_field("proof_hash", &self.proof_hash)?;
|
||||||
|
state.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::de::Deserialize<'de> for OutputPrintable {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where
|
||||||
|
D: serde::de::Deserializer<'de> {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(field_identifier, rename_all = "snake_case")]
|
||||||
|
enum Field {
|
||||||
|
OutputType,
|
||||||
|
Commit,
|
||||||
|
SwitchCommitHash,
|
||||||
|
Spent,
|
||||||
|
Proof,
|
||||||
|
ProofHash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct OutputPrintableVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for OutputPrintableVisitor {
|
||||||
|
type Value = OutputPrintable;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("a print able Output")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error> where
|
||||||
|
A: MapAccess<'de>, {
|
||||||
|
let mut output_type = None;
|
||||||
|
let mut commit = None;
|
||||||
|
let mut switch_commit_hash = None;
|
||||||
|
let mut spent = None;
|
||||||
|
let mut proof = None;
|
||||||
|
let mut proof_hash = None;
|
||||||
|
|
||||||
|
while let Some(key) = map.next_key()? {
|
||||||
|
match key {
|
||||||
|
Field::OutputType => {
|
||||||
|
no_dup!(output_type);
|
||||||
|
output_type = Some(map.next_value()?)
|
||||||
|
},
|
||||||
|
Field::Commit => {
|
||||||
|
no_dup!(commit);
|
||||||
|
|
||||||
|
let val: String = map.next_value()?;
|
||||||
|
let vec = util::from_hex(val.clone())
|
||||||
|
.map_err(serde::de::Error::custom)?;
|
||||||
|
commit = Some(pedersen::Commitment::from_vec(vec));
|
||||||
|
},
|
||||||
|
Field::SwitchCommitHash => {
|
||||||
|
no_dup!(switch_commit_hash);
|
||||||
|
|
||||||
|
let val: &str = map.next_value()?;
|
||||||
|
let hash = core::SwitchCommitHash::from_hex(val.clone())
|
||||||
|
.map_err(serde::de::Error::custom)?;
|
||||||
|
switch_commit_hash = Some(hash)
|
||||||
|
},
|
||||||
|
Field::Spent => {
|
||||||
|
no_dup!(spent);
|
||||||
|
spent = Some(map.next_value()?)
|
||||||
|
},
|
||||||
|
Field::Proof => {
|
||||||
|
no_dup!(proof);
|
||||||
|
|
||||||
|
let val: Option<String> = map.next_value()?;
|
||||||
|
|
||||||
|
if val.is_some() {
|
||||||
|
let vec = util::from_hex(val.unwrap().clone())
|
||||||
|
.map_err(serde::de::Error::custom)?;
|
||||||
|
let mut bytes = [0; MAX_PROOF_SIZE];
|
||||||
|
for i in 0..vec.len() {
|
||||||
|
bytes[i] = vec[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
proof = Some(pedersen::RangeProof { proof: bytes, plen: vec.len() })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Field::ProofHash => {
|
||||||
|
no_dup!(proof_hash);
|
||||||
|
proof_hash = Some(map.next_value()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(OutputPrintable {
|
||||||
|
output_type: output_type.unwrap(),
|
||||||
|
commit: commit.unwrap(),
|
||||||
|
switch_commit_hash: switch_commit_hash.unwrap(),
|
||||||
|
spent: spent.unwrap(),
|
||||||
|
proof: proof,
|
||||||
|
proof_hash: proof_hash.unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FIELDS: &'static [&'static str] = &["output_type", "commit", "switch_commit_hash", "spent", "proof", "proof_hash"];
|
||||||
|
deserializer.deserialize_struct("OutputPrintable", FIELDS, OutputPrintableVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,3 +585,26 @@ pub struct PoolInfo {
|
||||||
/// Total size of pool + orphans
|
/// Total size of pool + orphans
|
||||||
pub total_size: usize,
|
pub total_size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_output() {
|
||||||
|
let hex_output = "{\
|
||||||
|
\"output_type\":\"Coinbase\",\
|
||||||
|
\"commit\":\"083eafae5d61a85ab07b12e1a51b3918d8e6de11fc6cde641d54af53608aa77b9f\",\
|
||||||
|
\"switch_commit_hash\":\"85daaf11011dc11e52af84ebe78e2f2d19cbdc76000000000000000000000000\",\
|
||||||
|
\"spent\":false,\
|
||||||
|
\"proof\":null,\
|
||||||
|
\"proof_hash\":\"ed6ba96009b86173bade6a9227ed60422916593fa32dd6d78b25b7a4eeef4946\"\
|
||||||
|
}";
|
||||||
|
let deserialized: OutputPrintable = serde_json::from_str(&hex_output).unwrap();
|
||||||
|
let serialized = serde_json::to_string(&deserialized).unwrap();
|
||||||
|
assert_eq!(serialized, hex_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_utxo() {
|
||||||
|
let hex_commit = "{\"commit\":\"083eafae5d61a85ab07b12e1a51b3918d8e6de11fc6cde641d54af53608aa77b9f\"}";
|
||||||
|
let deserialized: Utxo = serde_json::from_str(&hex_commit).unwrap();
|
||||||
|
let serialized = serde_json::to_string(&deserialized).unwrap();
|
||||||
|
assert_eq!(serialized, hex_commit);
|
||||||
|
}
|
|
@ -115,10 +115,7 @@ fn refresh_missing_block_hashes(config: &WalletConfig, keychain: &Keychain) -> R
|
||||||
Ok(blocks) => {
|
Ok(blocks) => {
|
||||||
for block in blocks {
|
for block in blocks {
|
||||||
for out in block.outputs {
|
for out in block.outputs {
|
||||||
if let Ok(c) = util::from_hex(String::from(out.commit)) {
|
api_blocks.insert(out.commit, block.header.clone());
|
||||||
let commit = pedersen::Commitment::from_vec(c);
|
|
||||||
api_blocks.insert(commit, block.header.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +188,7 @@ fn refresh_output_state(config: &WalletConfig, keychain: &Keychain) -> Result<()
|
||||||
|
|
||||||
match api::client::get::<Vec<api::Utxo>>(url.as_str()) {
|
match api::client::get::<Vec<api::Utxo>>(url.as_str()) {
|
||||||
Ok(outputs) => for out in outputs {
|
Ok(outputs) => for out in outputs {
|
||||||
api_utxos.insert(out.commit, out);
|
api_utxos.insert(out.commit.commit(), out);
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// if we got anything other than 200 back from server, don't attempt to refresh
|
// if we got anything other than 200 back from server, don't attempt to refresh
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use keychain::{Keychain, Identifier};
|
use keychain::{Keychain, Identifier};
|
||||||
use util::{LOGGER, from_hex};
|
use util::{LOGGER, to_hex};
|
||||||
use util::secp::pedersen;
|
use util::secp::pedersen;
|
||||||
use api;
|
use api;
|
||||||
use core::global;
|
use core::global;
|
||||||
|
@ -175,13 +175,13 @@ fn find_utxos_with_key(
|
||||||
);
|
);
|
||||||
|
|
||||||
// add it to result set here
|
// add it to result set here
|
||||||
let commit_id = from_hex(output.commit.clone()).unwrap();
|
let commit_id = output.commit.0;
|
||||||
|
|
||||||
let res = retrieve_amount_and_coinbase_status(
|
let res = retrieve_amount_and_coinbase_status(
|
||||||
config,
|
config,
|
||||||
keychain,
|
keychain,
|
||||||
key_id.clone(),
|
key_id.clone(),
|
||||||
&output.commit,
|
&to_hex(output.commit.0.to_vec()),
|
||||||
block_outputs.header.height,
|
block_outputs.header.height,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue