

//WHITESPACE = _{ "" } // Prohibition of white spaces
nl = _{ "\n" } // Only one way to break a line is accepted

hexa_upper = { ASCII_DIGIT | 'A'..'F' }
base58 = { !("O" | "I" | "l") ~ ASCII_ALPHANUMERIC }
base64 = { ASCII_ALPHANUMERIC | "+" | "/" }
no_zero_u_int = @{ '1'..'9' ~ ASCII_DIGIT* }
u_int = @{ "0" | no_zero_u_int }

hash = @{ hexa_upper{64} }
currency = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "-" | "_")* }
pubkey = @{ base58{43,44} }
uid = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "-" | "_")* }
block_id = @{ u_int }
blockstamp =  ${ block_id ~ "-" ~ hash }
ed25519_sig = @{ base64{88} | (base64{87} ~ "=") | (base64{86} ~ "==") }

v10 = _{ "Version: 10" }

idty_v10 = ${
    SOI ~ v10 ~ nl ~
    "Type: Identity" ~ nl ~
    "Currency: " ~ currency ~ nl ~
    "Issuer: " ~ pubkey ~ nl ~
    "UniqueID: " ~ uid ~ nl ~
    "Timestamp: " ~ blockstamp ~ nl ~
    ed25519_sig? ~ nl*
    ~ EOI
}

idty = ${
    (&(SOI ~ v10 ~ nl) ~ idty_v10)
}

membership_in = @{ "IN" }
membership_out = @{ "OUT" }

membership_v10 = ${
    SOI ~ v10 ~ nl ~
    "Type: Membership" ~ nl ~
    "Currency: " ~ currency ~ nl ~
    "Issuer: " ~ pubkey ~ nl ~
    "Block: " ~ blockstamp ~ nl ~
    "Membership: " ~ (membership_in | membership_out) ~ nl ~
    "UserID: " ~ uid ~ nl ~
    "CertTS: " ~ blockstamp ~ nl ~
    ed25519_sig? ~ nl*
    ~ EOI
}

membership = ${
    (&(SOI ~ v10 ~ nl) ~ membership_v10)
}

cert_v10 = ${
    v10 ~ nl ~
    "Type: Certification" ~ nl ~
    "Currency: " ~ currency ~ nl ~
    "Issuer: " ~ pubkey ~ nl ~
    "IdtyIssuer: " ~ pubkey ~ nl ~
    "IdtyUniqueID: " ~ uid ~ nl ~
    "IdtyTimestamp: " ~ blockstamp ~ nl ~
    "IdtySignature: " ~ ed25519_sig ~ nl ~
    "CertTimestamp: " ~ blockstamp ~ nl ~
    ed25519_sig? ~ nl*
    ~ EOI
}

cert = ${
    (&(SOI ~ v10 ~ nl) ~ cert_v10)
}

revoc_v10 = ${
    SOI ~ v10 ~ nl ~
    "Type: Revocation" ~ nl ~
    "Currency: " ~ currency ~ nl ~
    "Issuer: " ~ pubkey ~ nl ~
    "IdtyUniqueID: " ~ uid ~ nl ~
    "IdtyTimestamp: " ~ blockstamp ~ nl ~
    "IdtySignature: " ~ ed25519_sig ~ nl ~
    ed25519_sig? ~ nl*
    ~ EOI
}

revoc = ${
    (&(SOI ~ v10 ~ nl) ~ revoc_v10)
}

tx_locktime = @{ u_int }
tx_amount =  @{ u_int }
tx_amount_base =  @{ u_int }
du_block_id = @{ u_int }
tx_index =  @{ u_int }
input_index = @{ u_int }
issuer_index = @{ u_int }
xhx_secret = @ { ASCII_ALPHANUMERIC+ }
csv_duration = @{ u_int }
cltv_timestamp = @{ u_int }

tx_input = ${ tx_input_du | tx_input_tx }
tx_input_du = ${ tx_amount ~ ":" ~ tx_amount_base ~ ":D:" ~ pubkey ~ ":" ~ du_block_id }
tx_input_tx = ${ tx_amount ~ ":" ~ tx_amount_base ~ ":T:" ~ hash ~ ":" ~ tx_index }

tx_unlock = ${ input_index ~ ":" ~ unlock_cond ~ (" " ~ unlock_cond)* }
unlock_cond = _{ unlock_sig | unlock_xhx }
unlock_sig = ${ "SIG(" ~ issuer_index ~ ")" }
unlock_xhx = ${ "XHX(" ~ xhx_secret ~ ")" }

output_single_cond = { output_cond_sig | output_cond_xhx | output_cond_csv | output_cond_cltv }
output_cond_sig = ${ "SIG(" ~ pubkey ~ ")"? }
output_cond_xhx = ${ "XHX(" ~ hash ~ ")"? }
output_cond_csv = ${ "CSV(" ~ csv_duration ~ ")"? }
output_cond_cltv = ${ "CLTV(" ~ cltv_timestamp ~ ")"? }

operation = _{ output_cond_op_and | output_cond_op_or }
output_cond_op_and       = { " && " }
output_cond_op_or        = { " || " }

output_conds = { output_conds_expr }
output_conds_expr = _{ term ~ (operation ~ term)* }
output_conds_brackets_expr = ${ "(" ~ output_conds_expr ~ ")" }
term = _{ output_single_cond | output_conds_brackets_expr }
tx_output = ${ tx_amount ~ ":" ~ tx_amount_base ~ ":" ~ output_conds }

tx_comment_char = {
    ASCII_ALPHANUMERIC | "." | " " | "-" | "_" | "\\" | ":" | "/" | ";" | "*" | "[" | "]" | "(" | ")" | "?" | "!" | "^" 
    | "+" | "=" | "@" | "&" | "~" | "#" | "{" | "}" | "|" | "<" | ">" | "%"
}
tx_comment = @{ tx_comment_char{0,255} }

tx_v10 = ${
    SOI ~ v10 ~ nl ~
    "Type: Transaction" ~ nl ~
    "Currency: " ~ currency ~ nl ~
    "Blockstamp: " ~ blockstamp ~ nl ~
    "Locktime: " ~ tx_locktime ~ nl ~
    "Issuers:" ~ nl ~ (pubkey ~ nl)+ ~
    "Inputs:" ~ nl ~ (tx_input ~ nl)+ ~
    "Unlocks:" ~ nl ~ (tx_unlock ~ nl)+ ~
    "Outputs:" ~ nl ~ (tx_output ~ nl)+ ~
    "Comment: " ~ tx_comment ~ nl ~
    (ed25519_sig ~ nl)* ~ // intermediate signatures (zero if the transaction has only one signature)
    ed25519_sig? ~ nl* // last signature
    ~ EOI
}

tx = ${
    (&(SOI ~ v10 ~ nl) ~ tx_v10)
}

document_v10 = ${
    &(SOI ~ v10 ~ nl) ~
    idty_v10 | 
    membership_v10 | 
    cert_v10 | 
    revoc_v10 |
    tx_v10
}

document = ${
    &SOI ~
    (&(v10  ~ nl) ~ document_v10)
}
