Skip to content
Snippets Groups Projects
Commit 72bac11c authored by Éloïs's avatar Éloïs
Browse files

Merge branch '59-tx-regex-fail-to-parse-all-cases' into 'dev'

Resolve "TX regex fail to parse all cases !"

Closes #59

See merge request !49
parents 9f118a9a ae501884
No related branches found
No related tags found
1 merge request!49Resolve "TX regex fail to parse all cases !"
...@@ -29,10 +29,10 @@ use Blockstamp; ...@@ -29,10 +29,10 @@ use Blockstamp;
lazy_static! { lazy_static! {
static ref TRANSACTION_REGEX_SIZE: &'static usize = &40_000_000; static ref TRANSACTION_REGEX_SIZE: &'static usize = &40_000_000;
static ref TRANSACTION_REGEX_BUILDER: &'static str = static ref TRANSACTION_REGEX_BUILDER: &'static str =
r"^Blockstamp: (?P<blockstamp>[0-9]+-[0-9A-F]{64})\nLocktime: (?P<locktime>[0-9]+)\nIssuers:(?P<issuers>(?:\n[1-9A-Za-z][^OIl]{43,44})+)Inputs:\n(?P<inputs>([0-9A-Za-z:]+\n)+)Unlocks:\n(?P<unlocks>([0-9]+:(SIG\([0-9]+\) ?|XHX\(\w+\) ?)+\n)+)Outputs:\n(?P<outputs>([0-9A-Za-z()&|: ]+\n)+)Comment: (?P<comment>[\\\w:/;*\[\]()?!^+=@&~#{}|<>%. -]{0,255})\n$"; r"^Blockstamp: (?P<blockstamp>[0-9]+-[0-9A-F]{64})\nLocktime: (?P<locktime>[0-9]+)\nIssuers:(?P<issuers>(?:\n[1-9A-Za-z][^OIl]{43,44})+)Inputs:\n(?P<inputs>([0-9A-Za-z:]+\n)+)Unlocks:\n(?P<unlocks>([0-9]+:(SIG\([0-9]+\) ?|XHX\(\w+\) ?)+\n)+)Outputs:\n(?P<outputs>([0-9A-Za-z()&|: ]+\n)+)Comment: (?P<comment>[\\\w:/;*\[\]()?!^+=@&~#{}|<>%. -]{0,255})\n";
static ref ISSUER_REGEX: Regex = Regex::new("(?P<issuer>[1-9A-Za-z][^OIl]{43,44})\n").unwrap(); static ref ISSUER_REGEX: Regex = Regex::new("(?P<issuer>[1-9A-Za-z]{43,44})\n").unwrap();
static ref D_INPUT_REGEX: Regex = Regex::new( static ref D_INPUT_REGEX: Regex = Regex::new(
"^(?P<amount>[1-9][0-9]*):(?P<base>[0-9]+):D:(?P<pubkey>[1-9A-Za-z][^OIl]{43,44}):(?P<block_number>[0-9]+)$" "^(?P<amount>[1-9][0-9]*):(?P<base>[0-9]+):D:(?P<pubkey>[1-9A-Za-z]{43,44}):(?P<block_number>[0-9]+)$"
).unwrap(); ).unwrap();
static ref T_INPUT_REGEX: Regex = Regex::new( static ref T_INPUT_REGEX: Regex = Regex::new(
"^(?P<amount>[1-9][0-9]*):(?P<base>[0-9]+):T:(?P<tx_hash>[0-9A-F]{64}):(?P<tx_index>[0-9]+)$" "^(?P<amount>[1-9][0-9]*):(?P<base>[0-9]+):T:(?P<tx_hash>[0-9A-F]{64}):(?P<tx_index>[0-9]+)$"
...@@ -43,7 +43,7 @@ lazy_static! { ...@@ -43,7 +43,7 @@ lazy_static! {
static ref UNLOCK_SIG_REGEX: Regex = static ref UNLOCK_SIG_REGEX: Regex =
Regex::new(r"^SIG\((?P<index>[0-9]+)\)$").unwrap(); Regex::new(r"^SIG\((?P<index>[0-9]+)\)$").unwrap();
static ref UNLOCK_XHX_REGEX: Regex = Regex::new(r"^XHX\((?P<code>\w+)\)$").unwrap(); static ref UNLOCK_XHX_REGEX: Regex = Regex::new(r"^XHX\((?P<code>\w+)\)$").unwrap();
static ref OUTPUT_COND_SIG_REGEX: Regex = Regex::new(r"^SIG\((?P<pubkey>[1-9A-Za-z][^OIl]{43,44})\)$").unwrap(); static ref OUTPUT_COND_SIG_REGEX: Regex = Regex::new(r"^SIG\((?P<pubkey>[1-9A-Za-z]{43,44})\)$").unwrap();
static ref OUTPUT_COND_XHX_REGEX: Regex = Regex::new(r"^XHX\((?P<hash>[0-9A-F]{64})\)$").unwrap(); static ref OUTPUT_COND_XHX_REGEX: Regex = Regex::new(r"^XHX\((?P<hash>[0-9A-F]{64})\)$").unwrap();
static ref OUTPUT_COND_CLTV_REGEX: Regex = Regex::new(r"^CLTV\((?P<timestamp>[0-9]+)\)$").unwrap(); static ref OUTPUT_COND_CLTV_REGEX: Regex = Regex::new(r"^CLTV\((?P<timestamp>[0-9]+)\)$").unwrap();
static ref OUTPUT_COND_CSV_REGEX: Regex = Regex::new(r"^CSV\((?P<timestamp>[0-9]+)\)$").unwrap(); static ref OUTPUT_COND_CSV_REGEX: Regex = Regex::new(r"^CSV\((?P<timestamp>[0-9]+)\)$").unwrap();
...@@ -51,12 +51,12 @@ lazy_static! { ...@@ -51,12 +51,12 @@ lazy_static! {
static ref OUPUT_CONDS_AND: Regex = Regex::new(r"^(?P<conditions_group_1>[0-9A-Za-z()&| ]+) && (?P<conditions_group_2>[0-9A-Za-z()&| ]+)$").unwrap(); static ref OUPUT_CONDS_AND: Regex = Regex::new(r"^(?P<conditions_group_1>[0-9A-Za-z()&| ]+) && (?P<conditions_group_2>[0-9A-Za-z()&| ]+)$").unwrap();
static ref OUPUT_CONDS_OR: Regex = Regex::new(r"^(?P<conditions_group_1>[0-9A-Za-z()&| ]+) \|\| (?P<conditions_group_2>[0-9A-Za-z()&| ]+)$").unwrap(); static ref OUPUT_CONDS_OR: Regex = Regex::new(r"^(?P<conditions_group_1>[0-9A-Za-z()&| ]+) \|\| (?P<conditions_group_2>[0-9A-Za-z()&| ]+)$").unwrap();
static ref OUTPUT_REGEX: Regex = Regex::new( static ref OUTPUT_REGEX: Regex = Regex::new(
"^(?P<amount>[1-9][0-9]+):(?P<base>[0-9]+):(?P<conditions>[0-9A-Za-z()&| ]+)$" "^(?P<amount>[1-9][0-9]*):(?P<base>[0-9]+):(?P<conditions>[0-9A-Za-z()&| ]+)$"
).unwrap(); ).unwrap();
} }
/// Wrap a transaction input /// Wrap a transaction input
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransactionInput { pub enum TransactionInput {
/// Universal Dividend Input /// Universal Dividend Input
D(isize, usize, ed25519::PublicKey, u64), D(isize, usize, ed25519::PublicKey, u64),
...@@ -78,7 +78,8 @@ impl ToString for TransactionInput { ...@@ -78,7 +78,8 @@ impl ToString for TransactionInput {
} }
impl TransactionInput { impl TransactionInput {
fn parse_from_str(source: &str) -> Result<TransactionInput, V10DocumentParsingError> { /// Parse Transaction Input from string.
pub fn parse_from_str(source: &str) -> Result<TransactionInput, V10DocumentParsingError> {
if let Some(caps) = D_INPUT_REGEX.captures(source) { if let Some(caps) = D_INPUT_REGEX.captures(source) {
let amount = &caps["amount"]; let amount = &caps["amount"];
let base = &caps["base"]; let base = &caps["base"];
...@@ -114,7 +115,7 @@ impl TransactionInput { ...@@ -114,7 +115,7 @@ impl TransactionInput {
} }
/// Wrap a transaction unlock proof /// Wrap a transaction unlock proof
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransactionUnlockProof { pub enum TransactionUnlockProof {
/// Indicates that the signature of the corresponding key is at the bottom of the document /// Indicates that the signature of the corresponding key is at the bottom of the document
Sig(usize), Sig(usize),
...@@ -150,7 +151,7 @@ impl TransactionUnlockProof { ...@@ -150,7 +151,7 @@ impl TransactionUnlockProof {
} }
/// Wrap a transaction unlocks input /// Wrap a transaction unlocks input
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransactionInputUnlocks { pub struct TransactionInputUnlocks {
/// Input index /// Input index
pub index: usize, pub index: usize,
...@@ -171,7 +172,10 @@ impl ToString for TransactionInputUnlocks { ...@@ -171,7 +172,10 @@ impl ToString for TransactionInputUnlocks {
} }
impl TransactionInputUnlocks { impl TransactionInputUnlocks {
fn parse_from_str(source: &str) -> Result<TransactionInputUnlocks, V10DocumentParsingError> { /// Parse Transaction Unlock from string.
pub fn parse_from_str(
source: &str,
) -> Result<TransactionInputUnlocks, V10DocumentParsingError> {
if let Some(caps) = UNLOCKS_REGEX.captures(source) { if let Some(caps) = UNLOCKS_REGEX.captures(source) {
let index = &caps["index"].parse().expect("fail to parse unlock index"); let index = &caps["index"].parse().expect("fail to parse unlock index");
let unlocks = &caps["unlocks"]; let unlocks = &caps["unlocks"];
...@@ -195,8 +199,8 @@ impl TransactionInputUnlocks { ...@@ -195,8 +199,8 @@ impl TransactionInputUnlocks {
} }
/// Wrap a transaction ouput condition /// Wrap a transaction ouput condition
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransactionOuputCondition { pub enum TransactionOutputCondition {
/// The consumption of funds will require a valid signature of the specified key /// The consumption of funds will require a valid signature of the specified key
Sig(ed25519::PublicKey), Sig(ed25519::PublicKey),
/// The consumption of funds will require to provide a code with the hash indicated /// The consumption of funds will require to provide a code with the hash indicated
...@@ -207,37 +211,37 @@ pub enum TransactionOuputCondition { ...@@ -207,37 +211,37 @@ pub enum TransactionOuputCondition {
Csv(u64), Csv(u64),
} }
impl ToString for TransactionOuputCondition { impl ToString for TransactionOutputCondition {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match *self { match *self {
TransactionOuputCondition::Sig(ref pubkey) => format!("SIG({})", pubkey), TransactionOutputCondition::Sig(ref pubkey) => format!("SIG({})", pubkey),
TransactionOuputCondition::Xhx(ref hash) => format!("XHX({})", hash), TransactionOutputCondition::Xhx(ref hash) => format!("XHX({})", hash),
TransactionOuputCondition::Cltv(timestamp) => format!("CLTV({})", timestamp), TransactionOutputCondition::Cltv(timestamp) => format!("CLTV({})", timestamp),
TransactionOuputCondition::Csv(duration) => format!("CSV({})", duration), TransactionOutputCondition::Csv(duration) => format!("CSV({})", duration),
} }
} }
} }
impl TransactionOuputCondition { impl TransactionOutputCondition {
fn parse_from_str(source: &str) -> Result<TransactionOuputCondition, V10DocumentParsingError> { fn parse_from_str(source: &str) -> Result<TransactionOutputCondition, V10DocumentParsingError> {
if let Some(caps) = OUTPUT_COND_SIG_REGEX.captures(source) { if let Some(caps) = OUTPUT_COND_SIG_REGEX.captures(source) {
Ok(TransactionOuputCondition::Sig( Ok(TransactionOutputCondition::Sig(
ed25519::PublicKey::from_base58(&caps["pubkey"]) ed25519::PublicKey::from_base58(&caps["pubkey"])
.expect("fail to parse SIG TransactionOuputCondition"), .expect("fail to parse SIG TransactionOutputCondition"),
)) ))
} else if let Some(caps) = OUTPUT_COND_XHX_REGEX.captures(source) { } else if let Some(caps) = OUTPUT_COND_XHX_REGEX.captures(source) {
Ok(TransactionOuputCondition::Xhx(String::from(&caps["hash"]))) Ok(TransactionOutputCondition::Xhx(String::from(&caps["hash"])))
} else if let Some(caps) = OUTPUT_COND_CLTV_REGEX.captures(source) { } else if let Some(caps) = OUTPUT_COND_CLTV_REGEX.captures(source) {
Ok(TransactionOuputCondition::Cltv( Ok(TransactionOutputCondition::Cltv(
caps["timestamp"] caps["timestamp"]
.parse() .parse()
.expect("fail to parse CLTV TransactionOuputCondition"), .expect("fail to parse CLTV TransactionOutputCondition"),
)) ))
} else if let Some(caps) = OUTPUT_COND_CSV_REGEX.captures(source) { } else if let Some(caps) = OUTPUT_COND_CSV_REGEX.captures(source) {
Ok(TransactionOuputCondition::Csv( Ok(TransactionOutputCondition::Csv(
caps["duration"] caps["duration"]
.parse() .parse()
.expect("fail to parse CSV TransactionOuputCondition"), .expect("fail to parse CSV TransactionOutputCondition"),
)) ))
} else { } else {
Err(V10DocumentParsingError::InvalidInnerFormat( Err(V10DocumentParsingError::InvalidInnerFormat(
...@@ -248,39 +252,39 @@ impl TransactionOuputCondition { ...@@ -248,39 +252,39 @@ impl TransactionOuputCondition {
} }
/// Wrap a transaction ouput condition group /// Wrap a transaction ouput condition group
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransactionOuputConditionGroup { pub enum TransactionOutputConditionGroup {
/// Single /// Single
Single(TransactionOuputCondition), Single(TransactionOutputCondition),
/// Brackets /// Brackets
Brackets(Box<TransactionOuputConditionGroup>), Brackets(Box<TransactionOutputConditionGroup>),
/// And operator /// And operator
And( And(
Box<TransactionOuputConditionGroup>, Box<TransactionOutputConditionGroup>,
Box<TransactionOuputConditionGroup>, Box<TransactionOutputConditionGroup>,
), ),
/// Or operator /// Or operator
Or( Or(
Box<TransactionOuputConditionGroup>, Box<TransactionOutputConditionGroup>,
Box<TransactionOuputConditionGroup>, Box<TransactionOutputConditionGroup>,
), ),
} }
impl ToString for TransactionOuputConditionGroup { impl ToString for TransactionOutputConditionGroup {
fn to_string(&self) -> String { fn to_string(&self) -> String {
match *self { match *self {
TransactionOuputConditionGroup::Single(ref condition) => condition.to_string(), TransactionOutputConditionGroup::Single(ref condition) => condition.to_string(),
TransactionOuputConditionGroup::Brackets(ref condition_group) => { TransactionOutputConditionGroup::Brackets(ref condition_group) => {
format!("({})", condition_group.deref().to_string()) format!("({})", condition_group.deref().to_string())
} }
TransactionOuputConditionGroup::And(ref condition_group_1, ref condition_group_2) => { TransactionOutputConditionGroup::And(ref condition_group_1, ref condition_group_2) => {
format!( format!(
"{} && {}", "{} && {}",
condition_group_1.deref().to_string(), condition_group_1.deref().to_string(),
condition_group_2.deref().to_string() condition_group_2.deref().to_string()
) )
} }
TransactionOuputConditionGroup::Or(ref condition_group_1, ref condition_group_2) => { TransactionOutputConditionGroup::Or(ref condition_group_1, ref condition_group_2) => {
format!( format!(
"{} || {}", "{} || {}",
condition_group_1.deref().to_string(), condition_group_1.deref().to_string(),
...@@ -291,33 +295,33 @@ impl ToString for TransactionOuputConditionGroup { ...@@ -291,33 +295,33 @@ impl ToString for TransactionOuputConditionGroup {
} }
} }
impl TransactionOuputConditionGroup { impl TransactionOutputConditionGroup {
fn parse_from_str( fn parse_from_str(
conditions: &str, conditions: &str,
) -> Result<TransactionOuputConditionGroup, V10DocumentParsingError> { ) -> Result<TransactionOutputConditionGroup, V10DocumentParsingError> {
if let Ok(single_condition) = TransactionOuputCondition::parse_from_str(conditions) { if let Ok(single_condition) = TransactionOutputCondition::parse_from_str(conditions) {
Ok(TransactionOuputConditionGroup::Single(single_condition)) Ok(TransactionOutputConditionGroup::Single(single_condition))
} else if let Some(caps) = OUPUT_CONDS_BRAKETS.captures(conditions) { } else if let Some(caps) = OUPUT_CONDS_BRAKETS.captures(conditions) {
let inner_conditions = let inner_conditions =
TransactionOuputConditionGroup::parse_from_str(&caps["conditions"])?; TransactionOutputConditionGroup::parse_from_str(&caps["conditions"])?;
Ok(TransactionOuputConditionGroup::Brackets(Box::new( Ok(TransactionOutputConditionGroup::Brackets(Box::new(
inner_conditions, inner_conditions,
))) )))
} else if let Some(caps) = OUPUT_CONDS_AND.captures(conditions) { } else if let Some(caps) = OUPUT_CONDS_AND.captures(conditions) {
let conditions_group_1 = let conditions_group_1 =
TransactionOuputConditionGroup::parse_from_str(&caps["conditions_group_1"])?; TransactionOutputConditionGroup::parse_from_str(&caps["conditions_group_1"])?;
let conditions_group_2 = let conditions_group_2 =
TransactionOuputConditionGroup::parse_from_str(&caps["conditions_group_2"])?; TransactionOutputConditionGroup::parse_from_str(&caps["conditions_group_2"])?;
Ok(TransactionOuputConditionGroup::And( Ok(TransactionOutputConditionGroup::And(
Box::new(conditions_group_1), Box::new(conditions_group_1),
Box::new(conditions_group_2), Box::new(conditions_group_2),
)) ))
} else if let Some(caps) = OUPUT_CONDS_OR.captures(conditions) { } else if let Some(caps) = OUPUT_CONDS_OR.captures(conditions) {
let conditions_group_1 = let conditions_group_1 =
TransactionOuputConditionGroup::parse_from_str(&caps["conditions_group_1"])?; TransactionOutputConditionGroup::parse_from_str(&caps["conditions_group_1"])?;
let conditions_group_2 = let conditions_group_2 =
TransactionOuputConditionGroup::parse_from_str(&caps["conditions_group_2"])?; TransactionOutputConditionGroup::parse_from_str(&caps["conditions_group_2"])?;
Ok(TransactionOuputConditionGroup::Or( Ok(TransactionOutputConditionGroup::Or(
Box::new(conditions_group_1), Box::new(conditions_group_1),
Box::new(conditions_group_2), Box::new(conditions_group_2),
)) ))
...@@ -331,17 +335,17 @@ impl TransactionOuputConditionGroup { ...@@ -331,17 +335,17 @@ impl TransactionOuputConditionGroup {
} }
/// Wrap a transaction ouput /// Wrap a transaction ouput
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransactionOuput { pub struct TransactionOutput {
/// Amount /// Amount
pub amount: isize, pub amount: isize,
/// Base /// Base
pub base: usize, pub base: usize,
/// List of conditions for consum this output /// List of conditions for consum this output
pub conditions: TransactionOuputConditionGroup, pub conditions: TransactionOutputConditionGroup,
} }
impl ToString for TransactionOuput { impl ToString for TransactionOutput {
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!( format!(
"{}:{}:{}", "{}:{}:{}",
...@@ -352,13 +356,14 @@ impl ToString for TransactionOuput { ...@@ -352,13 +356,14 @@ impl ToString for TransactionOuput {
} }
} }
impl TransactionOuput { impl TransactionOutput {
fn parse_from_str(source: &str) -> Result<TransactionOuput, V10DocumentParsingError> { /// Parse Transaction Ouput from string.
pub fn parse_from_str(source: &str) -> Result<TransactionOutput, V10DocumentParsingError> {
if let Some(caps) = OUTPUT_REGEX.captures(source) { if let Some(caps) = OUTPUT_REGEX.captures(source) {
let amount = caps["amount"].parse().expect("fail to parse output amount"); let amount = caps["amount"].parse().expect("fail to parse output amount");
let base = caps["base"].parse().expect("fail to parse base amount"); let base = caps["base"].parse().expect("fail to parse base amount");
let conditions = TransactionOuputConditionGroup::parse_from_str(&caps["conditions"])?; let conditions = TransactionOutputConditionGroup::parse_from_str(&caps["conditions"])?;
Ok(TransactionOuput { Ok(TransactionOutput {
conditions, conditions,
amount, amount,
base, base,
...@@ -374,7 +379,7 @@ impl TransactionOuput { ...@@ -374,7 +379,7 @@ impl TransactionOuput {
/// Wrap a Transaction document. /// Wrap a Transaction document.
/// ///
/// Must be created by parsing a text document or using a builder. /// Must be created by parsing a text document or using a builder.
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransactionDocument { pub struct TransactionDocument {
/// Document as text. /// Document as text.
/// ///
...@@ -395,7 +400,7 @@ pub struct TransactionDocument { ...@@ -395,7 +400,7 @@ pub struct TransactionDocument {
/// Inputs unlocks. /// Inputs unlocks.
unlocks: Vec<TransactionInputUnlocks>, unlocks: Vec<TransactionInputUnlocks>,
/// Transaction outputs. /// Transaction outputs.
outputs: Vec<TransactionOuput>, outputs: Vec<TransactionOutput>,
/// Transaction comment /// Transaction comment
comment: String, comment: String,
/// Document signature (there should be only one). /// Document signature (there should be only one).
...@@ -500,7 +505,7 @@ pub struct TransactionDocumentBuilder<'a> { ...@@ -500,7 +505,7 @@ pub struct TransactionDocumentBuilder<'a> {
/// Inputs unlocks. /// Inputs unlocks.
pub unlocks: &'a Vec<TransactionInputUnlocks>, pub unlocks: &'a Vec<TransactionInputUnlocks>,
/// Transaction ouputs. /// Transaction ouputs.
pub outputs: &'a Vec<TransactionOuput>, pub outputs: &'a Vec<TransactionOutput>,
/// Transaction comment /// Transaction comment
pub comment: &'a str, pub comment: &'a str,
} }
...@@ -631,7 +636,7 @@ impl StandardTextDocumentParser for TransactionDocumentParser { ...@@ -631,7 +636,7 @@ impl StandardTextDocumentParser for TransactionDocumentParser {
let mut outputs = Vec::new(); let mut outputs = Vec::new();
for output in outputs_array { for output in outputs_array {
if !output.is_empty() { if !output.is_empty() {
outputs.push(TransactionOuput::parse_from_str(output)?); outputs.push(TransactionOutput::parse_from_str(output)?);
} }
} }
...@@ -695,7 +700,7 @@ mod tests { ...@@ -695,7 +700,7 @@ mod tests {
.expect("fail to parse unlock !"), .expect("fail to parse unlock !"),
], ],
outputs: &vec![ outputs: &vec![
TransactionOuput::parse_from_str( TransactionOutput::parse_from_str(
"10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)", "10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)",
).expect("fail to parse output !"), ).expect("fail to parse output !"),
], ],
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment