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 @@
// 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/>.
//! 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;
@@ -25,12 +25,387 @@ use durs_blockchain_dal::*;
use durs_wot::*;
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)]
pub enum InvalidBlockError {
NoPreviousBlock,
VersionDecrease,
}
#[derive(Debug, Clone)]
///
pub enum TransactionDocumentError {
// todo
LengthError, // too_short or too_large
NumberSourcesError, // ...Error uselesse
UnlocksError,
SignatureIssuerError,
VersionError,
SignatureCountError, // SIGError(SIGError) existe déjà
SignaturesOrderError,
SignatureConstructionError,
VerificationResult(VerificationResult),
}
/*
impl From<VerificationResult> for TransactionDocumentError {
fn from(err: VerificationResult) -> Self {
VerificationResult(err)
}
}
*/
// #[derive(Debug, Clone, Fail)]
#[derive(Debug, Clone)]
// todo : documenter variante et fail
/// Local verification of a block error
pub enum LocalVerifyBlockError {
// todo
VerifyBlockHashsError, // invalidHash (expected_hash, actual_hash), verifiy inner hash à changer
/// the block version was not in the expected versions
Version {
expected_version: Vec<usize>,
actual_version: u32,
},
LocalVerifyGenesisBlockError(LocalVerifyGenesisBlockError),
LocalVerifyNotGenesisBlockError(LocalVerifyNotGenesisBlockError),
BlockTimeError,
ZerosHashBlockError,
SignaturesLenBlockError, // supprimer
SignatureBlockError, // SIGError, faudra faire une vérif sur le nb d'issuer "too many issuers"
SignatureMembershipDocumentError,
SignatureRevocationDocumentError,
TextDocumentFormat,
//#[fail(display = "blabl {}", tx_post)]
TransactionDocumentError(TransactionDocumentError), // innerTx document error{tx_post : usize, error : TransactionDocumentError }
/* TransactionDocumentError {
/// Protocole version
tx_post : usize,
error : TransactionDocumentError,
} */
}
// from tuple usize regarder gitlab
impl From<LocalVerifyGenesisBlockError> for LocalVerifyBlockError {
fn from(err: LocalVerifyGenesisBlockError) -> Self {
LocalVerifyGenesisBlockError(err)
}
}
// from tuple usize regarder gitlab
impl From<LocalVerifyNotGenesisBlockError> for LocalVerifyBlockError {
fn from(err: LocalVerifyNotGenesisBlockError) -> Self {
LocalVerifyNotGenesisBlockError(err)
}
}
// from tuple usize regarder gitlab
impl From<TransactionDocumentError> for LocalVerifyBlockError {
fn from(err: TransactionDocumentError) -> Self {
TransactionDocumentError(err)
}
}
use crate::dubp::check::LocalVerifyBlockError::*;
use crate::dubp::check::LocalVerifyGenesisBlockError::*;
use crate::dubp::check::LocalVerifyNotGenesisBlockError::*;
use crate::dubp::check::TransactionDocumentError::*;
#[derive(Debug, Clone)]
/// Local verification error particular of a Genesis Block
pub enum LocalVerifyGenesisBlockError {
//NotGenesisBlock,
/// A previous hash is provided
UnexpectedPreviousHash,
/// A previous issuer is provided
UnexpectedPreviousIssuer,
/// No paramters are provided
MissingParameters,
/// No dividend is provided
MissingDividend,
/// Unit base is not zero
NonZeroUnitBase,
/// The block time is different from the median time
BlockTimeDifferentFromMedianTime,
}
pub fn local_verification_genesis_block(
block: &BlockDocument,
) -> Result<(), (LocalVerifyGenesisBlockError)> {
/*
if block.number.0 > 0 {
return Err(NotGenesisBlock);
}
*/
// Previous Hash
if block.previous_hash.is_some() {
return Err(UnexpectedPreviousHash);
}
// Previous issuer
if block.previous_issuer.is_some() {
return Err(UnexpectedPreviousIssuer);
}
// Parameters
if block.parameters.is_none() {
return Err(MissingParameters);
}
// Universal Dividend
if block.dividend.is_none() {
return Err(MissingDividend);
}
// UnitBase
if block.unit_base > 0 {
return Err(NonZeroUnitBase);
}
// Date
if block.time != block.median_time {
return Err(BlockTimeDifferentFromMedianTime);
}
return Ok(());
}
#[derive(Debug, Clone)]
/// Local verification error particular of a not-genesis Block
pub enum LocalVerifyNotGenesisBlockError {
/// No previous hash is provided
MissingPreviousHash,
/// No previous issuer is provided
MissingPreviousIssuer,
/// Some Paramters are provided
UnexpectedParameters,
}
/// Verification of local rules of a block which is supposed to be not-genesis
pub fn local_verification_not_genesis_block(
block: &BlockDocument,
) -> Result<(), LocalVerifyNotGenesisBlockError> {
// PreviousHash
if block.previous_hash.is_none() {
return Err(MissingPreviousHash);
}
// PreviousIssuer
if block.previous_issuer.is_none() {
return Err(MissingPreviousIssuer);
}
// Parameters
if block.parameters.is_some() {
return Err(UnexpectedParameters);
}
return Ok(());
}
pub fn local_verification(block: &BlockDocument) -> Result<(), LocalVerifyBlockError> {
if block.number.0 == 0 {
// Verify local rules for a genesis block
local_verification_genesis_block(block)?;
} else {
// Verify local rules for a not genesis block
local_verification_not_genesis_block(block)?;
}
// Version
if !(block.version == 10 || block.version == 11) {
return Err(Version {
expected_version: vec![10, 11],
actual_version: block.version,
});
}
// InnerHash
// todo : + compliqué car faut changer le retour de verify
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); // plus explicite excpeted pattern 00000[0-3]* et le actual_hash format (hexa)
}
}
// 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),
} // récuperer les deux autres erreurs et renvoyer une SIG_error : invalid sig et
// 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) // rajouter currency parameters en argument Option utiliser unwrap!(option)
if block.time < block.median_time || block.median_time + max_acceleration < block.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 // pas une vérif locale en fait, erreur de conception
for x in &block.revoked {
match x {
dubp_documents::text_document_traits::TextDocumentFormat::Complete(y) => {
match y.verify_signatures() {
VerificationResult::Valid() => (),
_ => return Err(SignatureRevocationDocumentError),
} // deplacer dans compact // dans complete dire que c'est une erreur
}
dubp_documents::text_document_traits::TextDocumentFormat::Compact(y) => {
/*match y.verify_signatures() {
VerificationResult::Valid() => (),
_ => return Err(SignatureRevocationDocumentError),
}*/
// verify_signatures à implementer
}
_ => (), // compact n'existe pas // ne pas utiliser de _
}
}
// 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 10
if y.version() != 10 {
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))),
} // invalid -> invalid SIG, incomplete : dans les transactions même paramètre, pour les autres il faut indiquer invlaid nb of signatures
}
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>(
block: &BlockDocument,
blockchain_db: &BinDB<LocalBlockchainV10Datas>,
Loading