Encodable/Decodable improvements; add `user_enum` macro for string enums

This commit is contained in:
Andrew Poelstra 2014-09-05 09:10:18 -05:00
parent 331e555e4c
commit bc7077fac4
7 changed files with 90 additions and 44 deletions

View File

@ -159,6 +159,39 @@ macro_rules! impl_array_newtype(
} }
) )
macro_rules! impl_array_newtype_encodable(
($thing:ident, $ty:ty, $len:expr) => {
impl<D: ::serialize::Decoder<E>, E> ::serialize::Decodable<D, E> for $thing {
fn decode(d: &mut D) -> ::std::prelude::Result<$thing, E> {
use serialize::Decodable;
::assert_type_is_copy::<$ty>();
d.read_seq(|d, len| {
if len != $len {
Err(d.error("Invalid length"))
} else {
unsafe {
use std::mem;
let mut ret: [$ty, ..$len] = mem::uninitialized();
for i in range(0, len) {
ret[i] = try!(d.read_seq_elt(i, |d| Decodable::decode(d)));
}
Ok($thing(ret))
}
}
})
}
}
impl<E: ::serialize::Encoder<S>, S> ::serialize::Encodable<E, S> for $thing {
fn encode(&self, e: &mut E) -> ::std::prelude::Result<(), S> {
self.as_slice().encode(e)
}
}
}
)
macro_rules! impl_array_newtype_show( macro_rules! impl_array_newtype_show(
($thing:ident) => { ($thing:ident) => {
impl ::std::fmt::Show for $thing { impl ::std::fmt::Show for $thing {

View File

@ -68,3 +68,6 @@ pub mod blockdata;
pub mod util; pub mod util;
pub mod wallet; pub mod wallet;
/// I dunno where else to put this..
fn assert_type_is_copy<T: Copy>() { }

View File

@ -84,4 +84,35 @@ macro_rules! nu_select(
}) })
) )
#[macro_export]
macro_rules! user_enum(
($(#[$attr:meta])* pub enum $name:ident { $(#[$doc:meta] $elem:ident <-> $txt:expr),* }) => (
$(#[$attr])*
pub enum $name {
$(#[$doc] $elem),*
}
impl ::std::fmt::Show for $name {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
f.pad(match *self {
$($elem => $txt),*
})
}
}
impl<S: ::serialize::Encoder<E>, E> ::serialize::Encodable<S, E> for $name {
fn encode(&self, s: &mut S) -> Result<(), E> {
s.emit_str(self.to_string().as_slice())
}
}
impl <D: ::serialize::Decoder<E>, E> ::serialize::Decodable<D, E> for $name {
fn decode(d: &mut D) -> Result<$name, E> {
let s = try!(d.read_str());
$( if s.as_slice() == $txt { Ok($elem) } )else*
else { Err(d.error(format!("unknown `{}`", s).as_slice())) }
}
}
);
)

View File

@ -18,46 +18,19 @@
//! protocol, such as protocol versioning and magic header bytes. //! protocol, such as protocol versioning and magic header bytes.
//! //!
use std::fmt;
use serialize::{Decoder, Encoder, Encodable, Decodable};
use network::encodable::{ConsensusDecodable, ConsensusEncodable}; use network::encodable::{ConsensusDecodable, ConsensusEncodable};
use network::serialize::{SimpleEncoder, SimpleDecoder}; use network::serialize::{SimpleEncoder, SimpleDecoder};
/// The cryptocurrency to operate on user_enum!(
#[deriving(PartialEq, Eq, Clone, Hash)] #[deriving(PartialEq, Eq, Clone, Hash)]
#[doc="The cryptocurrency to act on"]
pub enum Network { pub enum Network {
/// Classic Bitcoin #[doc="Classic Bitcoin"]
Bitcoin, Bitcoin <-> "bitcoin",
/// Bitcoin's testnet #[doc="Bitcoin's testnet"]
BitcoinTestnet, BitcoinTestnet <-> "testnet"
}
impl fmt::Show for Network {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(match *self {
Bitcoin => "bitcoin",
BitcoinTestnet => "testnet",
})
}
}
impl<S:Encoder<E>, E> Encodable<S, E> for Network {
fn encode(&self, s: &mut S) -> Result<(), E> {
s.emit_str(self.to_string().as_slice())
}
}
impl <D:Decoder<E>, E> Decodable<D, E> for Network {
fn decode(d: &mut D) -> Result<Network, E> {
let s = try!(d.read_str());
match s.as_slice() {
"bitcoin" => Ok(Bitcoin),
"testnet" => Ok(BitcoinTestnet),
_ => Err(d.error(format!("Unknown network {}", s).as_slice()))
}
}
} }
)
pub static PROTOCOL_VERSION: u32 = 70001; pub static PROTOCOL_VERSION: u32 = 70001;
pub static SERVICES: u64 = 0; pub static SERVICES: u64 = 0;

View File

@ -28,7 +28,7 @@ use util::hash::Sha256dHash;
/// An address index /// An address index
#[deriving(Clone, PartialEq, Eq, Show)] #[deriving(Clone, PartialEq, Eq, Show)]
pub struct AddressIndex { pub struct AddressIndex {
index: HashMap<Script, (Sha256dHash, uint)> index: HashMap<Script, Vec<(Sha256dHash, uint)>>
} }
impl AddressIndex { impl AddressIndex {
@ -40,7 +40,9 @@ impl AddressIndex {
}; };
for (key, idx, txo) in utxo_set.iter() { for (key, idx, txo) in utxo_set.iter() {
if wallet.might_be_mine(txo) { if wallet.might_be_mine(txo) {
ret.index.insert(txo.script_pubkey.clone(), (key, idx)); ret.index.insert_or_update_with(txo.script_pubkey.clone(),
vec![(key, idx)],
|_, v| v.push((key, idx)));
} }
} }
ret ret

View File

@ -37,18 +37,20 @@ use util::base58::{Base58Error,
pub struct ChainCode([u8, ..32]); pub struct ChainCode([u8, ..32]);
impl_array_newtype!(ChainCode, u8, 32) impl_array_newtype!(ChainCode, u8, 32)
impl_array_newtype_show!(ChainCode) impl_array_newtype_show!(ChainCode)
impl_array_newtype_encodable!(ChainCode, u8, 32)
/// A fingerprint /// A fingerprint
pub struct Fingerprint([u8, ..4]); pub struct Fingerprint([u8, ..4]);
impl_array_newtype!(Fingerprint, u8, 4) impl_array_newtype!(Fingerprint, u8, 4)
impl_array_newtype_show!(Fingerprint) impl_array_newtype_show!(Fingerprint)
impl_array_newtype_encodable!(Fingerprint, u8, 4)
impl Default for Fingerprint { impl Default for Fingerprint {
fn default() -> Fingerprint { Fingerprint([0, 0, 0, 0]) } fn default() -> Fingerprint { Fingerprint([0, 0, 0, 0]) }
} }
/// Extended private key /// Extended private key
#[deriving(Clone, PartialEq, Eq, Show)] #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Show)]
pub struct ExtendedPrivKey { pub struct ExtendedPrivKey {
/// The network this key is to be used on /// The network this key is to be used on
pub network: Network, pub network: Network,
@ -65,7 +67,7 @@ pub struct ExtendedPrivKey {
} }
/// Extended public key /// Extended public key
#[deriving(Clone, PartialEq, Eq, Show)] #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Show)]
pub struct ExtendedPubKey { pub struct ExtendedPubKey {
/// The network this key is to be used on /// The network this key is to be used on
pub network: Network, pub network: Network,
@ -82,7 +84,7 @@ pub struct ExtendedPubKey {
} }
/// A child number for a derived key /// A child number for a derived key
#[deriving(Clone, PartialEq, Eq, Show)] #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Show)]
pub enum ChildNumber { pub enum ChildNumber {
/// Hardened key index, within [0, 2^31 - 1] /// Hardened key index, within [0, 2^31 - 1]
Hardened(u32), Hardened(u32),
@ -98,7 +100,9 @@ pub enum Error {
/// A secp256k1 error occured /// A secp256k1 error occured
EcdsaError(secp256k1::Error), EcdsaError(secp256k1::Error),
/// A child number was provided that was out of range /// A child number was provided that was out of range
InvalidChildNumber(ChildNumber) InvalidChildNumber(ChildNumber),
/// Error creating a master seed --- for application use
RngError(String)
} }
impl ExtendedPrivKey { impl ExtendedPrivKey {

View File

@ -34,7 +34,7 @@ pub enum Error {
} }
/// An account /// An account
#[deriving(Clone, PartialEq, Eq, Show)] #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Show)]
pub struct Account { pub struct Account {
name: String, name: String,
internal_path: Vec<ChildNumber>, internal_path: Vec<ChildNumber>,
@ -52,7 +52,7 @@ impl Default for Account {
} }
/// A wallet /// A wallet
#[deriving(Clone, PartialEq, Eq, Show)] #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Show)]
pub struct Wallet { pub struct Wallet {
master: ExtendedPrivKey, master: ExtendedPrivKey,
accounts: HashMap<String, Account> accounts: HashMap<String, Account>