Skip to content
Snippets Groups Projects

WIP: Resolve "DUBP local validation"

Closed jawaka requested to merge jawaka-146-dubp-local-validation into dev

Files

@@ -13,7 +13,7 @@
@@ -13,7 +13,7 @@
// You should have received a copy of the GNU Affero General Public License
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Sub-module checking if a block complies with all the rules of the (DUBP DUniter Blockchain Protocol).
//! Sub-module that checks and applies the content of a block according to the DUBP (DUBP DUniter Blockchain Protocol).
pub mod hashs;
pub mod hashs;
@@ -25,12 +25,289 @@ use durs_blockchain_dal::*;
@@ -25,12 +25,289 @@ use durs_blockchain_dal::*;
use durs_wot::*;
use durs_wot::*;
use std::collections::HashMap;
use std::collections::HashMap;
 
use dubp_documents::documents::block::TxDocOrTxHash;
 
use dubp_documents::text_document_traits::CompactTextDocument;
 
use dubp_documents::VerificationResult;
 
use durs_common_tools::fatal_error;
 
 
pub static ZERO_STRING: &'static str = "0";
 
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone)]
pub enum InvalidBlockError {
pub enum InvalidBlockError {
NoPreviousBlock,
NoPreviousBlock,
VersionDecrease,
VersionDecrease,
}
}
 
#[derive(Debug, Clone)]
 
pub enum TransactionDocumentError {
 
LengthError,
 
NumberSourcesError,
 
UnlocksError,
 
SignatureIssuerError,
 
VersionError,
 
SignatureCountError,
 
SignaturesOrderError,
 
SignatureConstructionError,
 
VerificationResult(VerificationResult),
 
}
 
 
/*
 
impl From<VerificationResult> for TransactionDocumentError {
 
fn from(err: VerificationResult) -> Self {
 
VerificationResult(err)
 
}
 
}
 
*/
 
 
#[derive(Debug, Clone)]
 
pub enum LocalVerifyBlockError {
 
VerifyBlockHashsError,
 
VersionBlockError,
 
PreviousIssuerRootBlockError,
 
ParametersRootBlockError,
 
UniversalDividendRootBlockError,
 
UnitBaseRootBlockError,
 
BlockTimeError,
 
ZerosHashBlockError,
 
SignaturesLenBlockError,
 
SignatureBlockError,
 
SignatureMembershipDocumentError,
 
SignatureRevocationDocumentError,
 
TextDocumentFormat,
 
TransactionDocumentError(TransactionDocumentError),
 
}
 
 
impl From<TransactionDocumentError> for LocalVerifyBlockError {
 
fn from(err: TransactionDocumentError) -> Self {
 
TransactionDocumentError(err)
 
}
 
}
 
 
use crate::dubp::check::LocalVerifyBlockError::*;
 
use crate::dubp::check::TransactionDocumentError::*;
 
 
 
 
pub fn local_verification(block: &BlockDocument) -> Result<(), LocalVerifyBlockError> {
 
 
// Version
 
if !(block.version == 10 || block.version == 11) {
 
return Err(VersionBlockError);
 
}
 
 
// InnerHash
 
if !block.verify_inner_hash() {
 
return Err(VerifyBlockHashsError);
 
}
 
 
// Nonce
 
// nothing to do
 
 
// Proof of work
 
if let Some(h) = block.hash {
 
let hash_string = h.0.to_hex();
 
let remainder = block.pow_min % 16;
 
let nb_zeros = (block.pow_min - remainder) / 16;
 
let repeated_zero_string = ZERO_STRING.repeat(nb_zeros);
 
if !hash_string.starts_with(&repeated_zero_string) {
 
return Err(ZerosHashBlockError);
 
}
 
}
 
 
// PreviousHash
 
// todo: solve ticket #150 (previous_hash must be an Option) before implementing this local verification
 
 
// PreviousIssuer
 
if block.number.0 > 0 && block.previous_issuer.is_none() {
 
return Err(PreviousIssuerRootBlockError);
 
}
 
if block.number.0 == 0 && block.previous_issuer.is_some() {
 
return Err(PreviousIssuerRootBlockError);
 
}
 
 
// Parameters
 
if block.number.0 == 0 && block.parameters.is_none() {
 
return Err(ParametersRootBlockError);
 
}
 
if block.number.0 > 0 && block.parameters.is_some() {
 
return Err(ParametersRootBlockError);
 
}
 
 
// Universal Dividend
 
if block.number.0 == 0 && block.dividend.is_none() {
 
return Err(UniversalDividendRootBlockError);
 
}
 
 
// UnitBase
 
if block.number.0 == 0 && block.unit_base > 0 {
 
return Err(UnitBaseRootBlockError);
 
}
 
 
// Signature
 
if block.signatures.len() != 1 {
 
// optimisable en ne calculant pas toute la longueur du tableau ?
 
return Err(SignaturesLenBlockError);
 
}
 
 
match block.verify_signatures() {
 
VerificationResult::Valid() => (),
 
_ => return Err(SignatureBlockError),
 
}
 
 
// Dates
 
//let avg_gen_time = block.parameters.avg_gen_time as f64;
 
//let maxGenTime = avg_gen_time * 1.189;
 
let max_acceleration = 1; // todo: max_acceleration = CEIL(maxGenTime * median_time_blocks)
 
 
if block.time < block.median_time || block.median_time + max_acceleration < block.time {
 
return Err(BlockTimeError);
 
}
 
if block.number.0 == 0 && block.time != block.median_time {
 
return Err(BlockTimeError);
 
}
 
 
// Identities
 
for x in &block.identities {
 
match x.verify_signatures() {
 
VerificationResult::Valid() => (),
 
_ => return Err(SignatureBlockError),
 
}
 
}
 
 
// Memberships (Joiners, Actives, Leavers)
 
for x in &block.joiners {
 
match x.verify_signatures() {
 
VerificationResult::Valid() => (),
 
_ => return Err(SignatureMembershipDocumentError),
 
}
 
}
 
 
for x in &block.actives {
 
match x.verify_signatures() {
 
VerificationResult::Valid() => (),
 
_ => return Err(SignatureMembershipDocumentError),
 
}
 
}
 
 
for x in &block.leavers {
 
match x.verify_signatures() {
 
VerificationResult::Valid() => (),
 
_ => return Err(SignatureMembershipDocumentError),
 
}
 
}
 
 
// Revoked
 
for x in &block.revoked {
 
match x {
 
dubp_documents::text_document_traits::TextDocumentFormat::Complete(y) => {
 
match y.verify_signatures() {
 
VerificationResult::Valid() => (),
 
_ => return Err(SignatureRevocationDocumentError),
 
}
 
}
 
dubp_documents::text_document_traits::TextDocumentFormat::Compact(y) => {
 
y
 
match y.verify_signatures() {
 
VerificationResult::Valid() => (),
 
_ => return Err(SignatureRevocationDocumentError),
 
}
 
 
}
 
_ => (), // question : ça peut arriver un compact ?
 
}
 
}
 
 
// Transactions
 
for x in &block.transactions {
 
match x {
 
TxDocOrTxHash::TxDoc(y) => {
 
// A transaction in compact format cannot measure more than 100 lines
 
if y.as_compact_text().lines().count() >= 100 {
 
// question : c'est strict ?
 
return Err(TransactionDocumentError(LengthError));
 
}
 
// A transaction must have at least 1 source
 
if y.get_inputs().is_empty() {
 
return Err(TransactionDocumentError(NumberSourcesError));
 
}
 
// A transaction cannot have `SIG(INDEX)` unlocks with `INDEX >= ` issuers count. ??
 
if y.get_unlocks().len() >= y.issuers().len() {
 
return Err(TransactionDocumentError(SignatureIssuerError));
 
}
 
// A transaction's version must be equal to 3
 
if y.version() != 3 {
 
// question : c'est certainement >= 3 vu les tests
 
return Err(TransactionDocumentError(VersionError));
 
}
 
 
// Signatures count must be the same as issuers count
 
if y.signatures().len() != y.issuers().len() {
 
return Err(TransactionDocumentError(SignatureCountError));
 
}
 
 
/*
 
* A transaction **must** have signatures matching its content **for each issuer**
 
* Signatures are ordered by issuer
 
* Signatures are made over the transaction's content, signatures excepted
 
*/
 
let result = y.verify_signatures();
 
match result {
 
VerificationResult::Valid() => (),
 
_ => return Err(TransactionDocumentError(VerificationResult(result))),
 
}
 
 
 
}
 
TxDocOrTxHash::TxHash(y) => {
 
fatal_error!("Block contains a Transaction Hash (and not a Transaction Document)")
 
}
 
}
 
}
 
 
Ok(())
 
}
 
 
#[cfg(test)]
 
mod tests {
 
 
#[test]
 
fn generate_and_verify_block() {
 
let mut block = BlockDocument {
 
nonce: 0,
 
version: 10,
 
number: BlockNumber(107_984),
 
pow_min: 88,
 
time: 1_522_685_861,
 
median_time: 1522683184,
 
members_count: 896,
 
monetary_mass: 140_469_765,
 
unit_base: 0,
 
issuers_count: 42,
 
issuers_frame: 211,
 
issuers_frame_var: 0,
 
currency: CurrencyName(String::from("g1")),
 
issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58("DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ").unwrap())],
 
signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64("92id58VmkhgVNee4LDqBGSm8u/ooHzAD67JM6fhAE/CV8LCz7XrMF1DvRl+eRpmlaVkp6I+Iy8gmZ1WUM5C8BA==").unwrap())],
 
hash: None,
 
parameters: None,
 
previous_hash: Hash::from_hex("000001144968D0C3516BE6225E4662F182E28956AF46DD7FB228E3D0F9413FEB").expect("fail to parse previous_hash"),
 
previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58("D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH").unwrap())),
 
inner_hash: None,
 
dividend: None,
 
identities: Vec::new(),
 
joiners: Vec::new(),
 
actives: Vec::new(),
 
leavers: Vec::new(),
 
revoked: Vec::new(),
 
excluded: Vec::new(),
 
certifications: vec![TextDocumentFormat::Complete(cert1)],
 
transactions: vec![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))],
 
inner_hash_and_nonce_str: String::new(),
 
};
 
assert_eq!(local_verification(&block), Ok);
 
}
 
}
 
pub fn verify_block_validity<W: WebOfTrust>(
pub fn verify_block_validity<W: WebOfTrust>(
block: &BlockDocument,
block: &BlockDocument,
blockchain_db: &BinDB<LocalBlockchainV10Datas>,
blockchain_db: &BinDB<LocalBlockchainV10Datas>,
Loading