Repository: LNP-BP/client_side_validation (language: rust)

Issue:
Finalize trust resolvers API



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 105b6d15..e2ed2469 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -32,7 +32,9 @@ jobs:
           - rand
           - crypto
           - serde
+          # - async
           - bitcoin
+          - rand,crypto,bitcoin
           - all
     steps:
       - uses: actions/checkout@v2
diff --git a/.github/workflows/pre-publish.yml b/.github/workflows/pre-publish.yml
index 3a1b53f9..7298cc16 100644
--- a/.github/workflows/pre-publish.yml
+++ b/.github/workflows/pre-publish.yml
@@ -2,7 +2,6 @@ name: Pre-publishing check
 
 on:
   pull_request:
-    branches: [ master ]
     tags:
       - 'pre-publish'
 
diff --git a/.github/workflows/publication.yml b/.github/workflows/publication.yml
index 58c84436..7cce015d 100644
--- a/.github/workflows/publication.yml
+++ b/.github/workflows/publication.yml
@@ -14,4 +14,8 @@ jobs:
     steps:
       - uses: actions/checkout@v2
       - run: cargo login ${{ secrets.CRATES_IO_TOKEN }}
+      - run: cargo publish --manifest-path single_use_seals/Cargo.toml --all-features
+      - run: cargo publish --manifest-path strict_encoding/derive/Cargo.toml --all-features
+      - run: cargo publish --manifest-path strict_encoding/Cargo.toml --all-features
+      - run: cargo publish --manifest-path commit_verify/Cargo.toml --all-features
       - run: cargo publish --all-features
diff --git a/Cargo.lock b/Cargo.lock
index 860db626..a877fb8d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -134,7 +134,7 @@ version = "0.5.0-rc.1"
 dependencies = [
  "commit_verify",
  "single_use_seals",
- "strict_encoding 1.5.0-beta.1",
+ "strict_encoding 1.5.0",
 ]
 
 [[package]]
@@ -148,7 +148,7 @@ dependencies = [
 
 [[package]]
 name = "commit_verify"
-version = "0.5.0-rc.1"
+version = "0.5.0"
 dependencies = [
  "amplify",
  "bitcoin_hashes",
@@ -624,7 +624,7 @@ dependencies = [
 
 [[package]]
 name = "strict_encoding"
-version = "1.5.0-beta.1"
+version = "1.5.0"
 dependencies = [
  "amplify",
  "bitcoin",
@@ -634,7 +634,7 @@ dependencies = [
  "grin_secp256k1zkp",
  "miniscript",
  "rand 0.7.3",
- "strict_encoding_derive 1.1.1",
+ "strict_encoding_derive 1.5.0",
 ]
 
 [[package]]
@@ -651,13 +651,13 @@ dependencies = [
 
 [[package]]
 name = "strict_encoding_derive"
-version = "1.1.1"
+version = "1.5.0"
 dependencies = [
  "amplify",
  "amplify_derive",
  "proc-macro2",
  "quote",
- "strict_encoding 1.5.0-beta.1",
+ "strict_encoding 1.5.0",
  "syn",
 ]
 
diff --git a/Cargo.toml b/Cargo.toml
index 729a5801..3823123f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,12 +17,12 @@ name = "client_side_validation"
 path = "src/lib.rs"
 
 [dependencies]
-strict_encoding = { version = "1.5.0-beta.1", path = "./strict_encoding" }
-commit_verify = { version = "0.5.0-rc.1", path = "./commit_verify" }
-single_use_seals = { version = "0.5.0-rc.1", path = "./single_use_seals" }
+strict_encoding = { version = "1.5.0", path = "./strict_encoding" }
+commit_verify = { version = "0.5.0", path = "./commit_verify" }
+single_use_seals = { version = "0.5.0", path = "./single_use_seals" }
 
 [features]
-default = ["crypto"]
+default = []
 all = ["serde", "rand", "bitcoin"]
 rand = ["commit_verify/rand"]
 serde = ["commit_verify/serde"]
diff --git a/README.md b/README.md
index 5891e22e..458c1f24 100644
--- a/README.md
+++ b/README.md
@@ -41,13 +41,16 @@ and [LNP/BP tech talks videos](https://www.youtube.com/channel/UCK_Q3xcQ-H3ERwAr
 This library consists of the following main three components, which define
 independent parts constituting together client-side-validation API and its core
 functionality. These are:
-- Strict encoding ([LNPBP-7] and [LNPBP-42] standards): binary standard of  
-  encoding client-side-validated data and network addresses
-- Commit-verify scheme and its client-side-validation specific implementations
-  * consensus commitments ([LNPBP-9] standard)
+- [Strict encoding](strict_encoding/README.md) ([LNPBP-7] and [LNPBP-42] 
+  standards): binary standard of encoding client-side-validated data and network 
+  addresses.
+- [Commit-verify](commit_verify/README.md) client-side-validation-specific APIs:
+  * consensus commitments (part of [LNPBP-8] standard)
   * multi-commitments ([LNPBP-4] standard)
   * merklization for client-side-validation ([LNPBP-81] standard)
-- Single-use-seals ([LNPBP-8] standard)
+- [Single-use-seals](single_use_seals/README.md) API ([LNPBP-8] standard)
+- Client-side-validation API from the library root, linking those components 
+  together according to [LNPBP-9] standard.
 
 
 ## Usage
@@ -79,14 +82,15 @@ default-features = false
 features = [] # Your set of features goes here
 ```
 
-The library has just a three feature flags, all of which are not used by default:
+The library has four feature flags, all of which are not used by default:
 - `rand`, providing support for generating random 32-byte sequences of `Slice32`
   type, used in many LNP/BP applications (for instance as hash-lock preimages or
   during LNPBP-4 multi-commitments);
 - `serde`, providing support for data structure serialization with serde across
   all library;
-- `crypto`, adding strict encoding support for Ed25519/X25519 and Grin 
-  Secp256k1zkp Pedersen commitments and bulletproofs data types.
+- `bitcoin`, providing implementation of strint encoding for bitcoin data types;
+- `crypto`, adding strict encoding support for Ed25519/X25519, Grin Secp256k1zkp
+  Pedersen commitments and bulletproofs data types.
 
 For specific features which may be enabled for the libraries, please check
 library-specific guidelines, located in `README.md` files in each of library
diff --git a/codecov.yml b/codecov.yml
index 8247d95d..240d7310 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -14,6 +14,6 @@ coverage:
           - master
     patch:
       default:
-        target: 80%
+        target: 60%
         threshold: 1%
         only_pulls: true
diff --git a/commit_verify/Cargo.toml b/commit_verify/Cargo.toml
index e36c9327..d9011d09 100644
--- a/commit_verify/Cargo.toml
+++ b/commit_verify/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "commit_verify"
-version = "0.5.0-rc.1"
+version = "0.5.0"
 license = "Apache-2.0"
 authors = ["Dr. Maxim Orlovsky <orlovsky@pandoracore.com>"]
 description = "Commit-verify API for client-side validation"
diff --git a/single_use_seals/Cargo.toml b/single_use_seals/Cargo.toml
index ff556b1b..2e3213c5 100644
--- a/single_use_seals/Cargo.toml
+++ b/single_use_seals/Cargo.toml
@@ -17,3 +17,8 @@ path = "src/lib.rs"
 
 [dependencies]
 amplify_derive = "2.7"
+
+[features]
+default = []
+all = [] # ["async"]
+# async = [] // When async will become part of MSRV
diff --git a/single_use_seals/src/lib.rs b/single_use_seals/src/lib.rs
index a66829ba..7d18b7c1 100644
--- a/single_use_seals/src/lib.rs
+++ b/single_use_seals/src/lib.rs
@@ -54,7 +54,7 @@
 //! **Single-use-seal**: a commitment to commit to some (potentially unknown)
 //!   message. The first commitment (i.e. single-use-seal) must be a
 //!   well-defined (i.e. fully specified and unequally identifiable
-//!   in some space, like in time/plance or within a given formal informational
+//!   in some space, like in time/place or within a given formal informational
 //!   system).
 //! **Closing of a single-use-seal over message**: a fulfilment of the first
 //!   commitment: creation of the actual commitment to some message in a form
@@ -74,22 +74,22 @@
 //!
 //! ## Trait structure
 //!
-//! The module defines trait [SingleUseSeal] that can be used for implementation
-//! of single-use-seals with methods for seal close and verification. A type
-//! implementing this trait operates only with messages (which is represented
-//! by [Message] type alias – in fact any type that implements `AsRef<[u8]>`,
-//! i.e. can be represented as a sequence of bytes) and witnesses (which is
-//! represented by an associated type [SingleUseSeal::Witness]). At the same
-//! time, [SingleUseSeal] can't define seals by itself — and also knows nothing
+//! The module defines trait [`SingleUseSeal`] that can be used for
+//! implementation of single-use-seals with methods for seal close and
+//! verification. A type implementing this trait operates only with messages
+//! (which is represented by any type that implements `AsRef<[u8]>`,i.e. can be
+//! represented as a sequence of bytes) and witnesses (which is represented by
+//! an associated type [`SingleUseSeal::Witness`]). At the same time,
+//! [`SingleUseSeal`] can't define seals by itself — and also knows nothing
 //! about whether the seal is in fact closed: this requires a "seal medium": a
 //! proof of publication medium on which the seals are defined.
 //!
-//! The module provides two options of implementing sch medium: synchonous
-//! [SealMedium] and asynchronous [AsyncSealMedium].
+//! The module provides two options of implementing sch medium: synchronous
+//! [`SealMedium`] and asynchronous [`AsyncSealMedium`].
 //!
 //! ## Sample implementation
 //!
-//! Examples of implementations can be found in [bp::seals][crate::bp::seals]
+//! Examples of implementations can be found in [`bp::seals`][crate::bp::seals]
 //! module of the crate source code.
 //!
 //! ## Further reading
@@ -104,9 +104,6 @@
 #[macro_use]
 extern crate amplify_derive;
 
-/// Message type that can be used to close the seal over it
-pub type Message = dyn AsRef<[u8]>;
-
 /// Single-use-seal trait: implement for a data structure that will hold a
 /// single-use-seal definition and will contain a business logic for closing
 /// seal over some message and verification of the seal against the message
@@ -114,7 +111,8 @@ pub type Message = dyn AsRef<[u8]>;
 ///
 /// NB: It is recommended that single-use-seal instances to be instantiated
 /// not by a constructor, but by a factory, i.e. "seal medium": data type
-/// implementing either [SealMedium] or [AsyncSealMedium] traits.
+/// implementing either [`SealMedium`] or [`AsyncSealMedium`] traits.
+#[cfg_attr(feature = "async", async_trait)]
 pub trait SingleUseSeal {
     /// Associated type for the witness produced by the single-use-seal close
     /// procedure
@@ -131,14 +129,17 @@ pub trait SingleUseSeal {
     /// NB: Closing of the seal MUST not change the internal state of the
     /// seal itself; all the data produced by the process must be placed
     /// into the returned Witness type
-    fn close(&self, over: &Message) -> Result<Self::Witness, Self::Error>;
+    fn close(
+        &self,
+        over: impl AsRef<[u8]>,
+    ) -> Result<Self::Witness, Self::Error>;
 
     /// Verifies that the seal was indeed closed over the message on the
-    /// specific seal medium (see [`SealMedium]`
+    /// specific seal medium (see [`SealMedium`])
     #[cfg(not(feature = "async"))]
     fn verify(
         &self,
-        msg: &Message,
+        msg: impl AsRef<[u8]>,
         witness: &Self::Witness,
         medium: &impl SealMedium<Self>,
     ) -> Result<bool, Self::Error>
@@ -146,11 +147,11 @@ pub trait SingleUseSeal {
         Self: Sized;
 
     /// Verifies that the seal was indeed closed over the message on the
-    /// specific seal medium (see [`SealMedium]`
+    /// specific seal medium (see [`SealMedium`])
     #[cfg(feature = "async")]
     async fn verify(
         &self,
-        msg: &Message,
+        msg: impl AsRef<[u8]>,
         witness: &Self::Witness,
         medium: &impl SealMedium<Self>,
     ) -> Result<bool, Self::Error>
@@ -167,7 +168,7 @@ pub trait SingleUseSeal {
 ///
 /// Since the medium may require network communications or extensive computing
 /// involved (like in case with blockchain) there is a special asynchronous
-/// version of the SealMedium [AsyncSealMedium], which requires use of
+/// version of the SealMedium [`AsyncSealMedium`], which requires use of
 /// `async` feature of this crate.
 ///
 /// All these operations are medium-specific; for the same single-use-seal type
@@ -181,7 +182,7 @@ where
     Seal: SingleUseSeal,
 {
     /// Publication id that may be used for referencing publication of
-    /// witness data in the medium. By default set `()`, so [SealMedium]
+    /// witness data in the medium. By default set `()`, so [`SealMedium`]
     /// may not implement  publication id and related functions
     type PublicationId;
 
@@ -200,7 +201,7 @@ where
 
     /// Publishes witness data to the medium. Function has default
     /// implementation doing nothing and returning
-    /// [SealMediumError::PublicationIdNotSupported] error.
+    /// [`SealMediumError::PublicationIdNotSupported`] error.
     fn publish_witness(
         &mut self,
         _witness: &Seal::Witness,
@@ -230,7 +231,7 @@ where
     }
 }
 
-/// Asynchronous version of the [SealMedium] trait.
+/// Asynchronous version of the [`SealMedium`] trait.
 #[cfg(feature = "async")]
 #[async_trait]
 pub trait SealMedium<Seal>
@@ -240,7 +241,7 @@ where
     Self::PublicationId: Sync,
 {
     /// Publication id that may be used for referencing publication of
-    /// witness data in the medium. By default set `()`, so [SealMedium]
+    /// witness data in the medium. By default set `()`, so [`SealMedium`]
     /// may not implement  publication id and related functions
     type PublicationId;
 
@@ -260,7 +261,7 @@ where
 
     /// Publishes witness data to the medium. Function has default
     /// implementation doing nothing and returning
-    /// [SealMediumError::PublicationIdNotSupported] error.
+    /// [`SealMediumError::PublicationIdNotSupported`] error.
     async fn publish_witness(
         &mut self,
         _witness: &Seal::Witness,
@@ -271,10 +272,10 @@ where
         Err(SealMediumError::PublicationIdNotSupported)
     }
 
-    /// Returns [Self::PublicationId] for a given witness, if any; the id is
+    /// Returns [`Self::PublicationId`] for a given witness, if any; the id is
     /// returned as an option. Function has default implementation doing
-    /// nothing and just returning [SealMediumError::PublicationIdNotSupported]
-    /// error.
+    /// nothing and just returning
+    /// [`SealMediumError::PublicationIdNotSupported`] error.
     async fn get_witness_publication_id(
         &self,
         _witness: &Seal::Witness,
@@ -287,7 +288,7 @@ where
 
     /// Validates whether a given publication id is present in the medium.
     /// Function has default implementation doing nothing and returning
-    /// [SealMediumError::PublicationIdNotSupported] error.
+    /// [`SealMediumError::PublicationIdNotSupported`] error.
     async fn validate_publication_id(
         &self,
         _publication_id: &Self::PublicationId,
@@ -299,8 +300,8 @@ where
     }
 }
 
-/// Single-use-seal status returned by [SealMedium::get_seal_status] and
-/// [AsyncSealMedium::get_seal_status] functions.
+/// Single-use-seal status returned by [`SealMedium::get_seal_status`] and
+/// [`AsyncSealMedium::get_seal_status`] functions.
 ///
 /// NB: It's important to note, that while its possible to deterministically
 ///   define was a given seal closed it yet may be not possible to find out
@@ -321,15 +322,15 @@ pub enum SealStatus {
     Closed = 1,
 }
 
-/// Error returned by [SealMedium] and [AsyncSealMedium] functions related
-/// to work with publication id ([SealMedium::PublicationId]). Required since
-/// not all implementation of [SealMedia] may define publication identifier,
+/// Error returned by [`SealMedium`] and [`AsyncSealMedium`] functions related
+/// to work with publication id ([`SealMedium::PublicationId`]). Required since
+/// not all implementation of [`SealMedia`] may define publication identifier,
 /// and the traits provide default implementation for these functions always
-/// returning [SealMediumError::OperationNotSupported]. If the implementation
+/// returning [`SealMediumError::OperationNotSupported`]. If the implementation
 /// would like to provide custom implementation, it may embed standard error
-/// related to [SealMedium] operations within
-/// [SealMediumError::MediumAccessError] case; the type of MediumAccessError is
-/// defined through generic argument to [SealMediumError].
+/// related to [`SealMedium`] operations within
+/// [`SealMediumError::MediumAccessError`] case; the type of MediumAccessError
+/// is defined through generic argument to [`SealMediumError`].
 #[derive(Clone, Copy, Debug, Display, Error, From)]
 #[display(doc_comments)]
 pub enum SealMediumError<M: std::error::Error> {
diff --git a/src/api.rs b/src/api.rs
new file mode 100644
index 00000000..f13ee54b
--- /dev/null
+++ b/src/api.rs
@@ -0,0 +1,544 @@
+// LNP/BP client-side-validation foundation libraries implementing LNPBP
+// specifications & standards (LNPBP-4, 7, 8, 9, 42, 81)
+//
+// Written in 2019-2021 by
+//     Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to
+// the public domain worldwide. This software is distributed without
+// any warranty.
+//
+// You should have received a copy of the Apache 2.0 License along with this
+// software. If not, see <https://opensource.org/licenses/Apache-2.0>.
+
+use std::fmt::{self, Debug, Display, Formatter};
+use std::hash::Hash;
+use std::iter::FromIterator;
+use std::ops::AddAssign;
+
+use single_use_seals::SingleUseSeal;
+use strict_encoding::{StrictDecode, StrictEncode};
+
+/// Result of client-side validation operation
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[repr(u8)]
+pub enum Validity {
+    /// The data are valid both in terms of the internal consistency and in
+    /// terms of commitments in the single-use-seals medium
+    Valid = 1,
+
+    /// The data are internally consistent and valid, but there were (multiple)
+    /// issues with resolving single-use-seals reported by the provided seal
+    /// resolver. These issues are not failed single-use-seals, but rather
+    /// errors accessing single-use-seals commitment medium about its status
+    /// (networking issues, transaction present in mempool and
+    /// not yet mined etc).
+    SealIssues = 0xFF,
+
+    /// The data are internally inconsistent/invalid
+    Invalid = 0,
+}
+
+impl Display for Validity {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        match self {
+            Validity::Valid => f.write_str("valid"),
+            Validity::SealIssues => f.write_str("unresolved seal issues"),
+            Validity::Invalid => f.write_str("invalid"),
+        }
+    }
+}
+
+/// Marker trait for all types of validation log entries (failures, trust
+/// issues, warnings, info messages) contained within a [`ValidationReport`]
+/// produced during client-side-validation.
+pub trait ValidationLog:
+    Clone + Eq + Hash + Debug + Display + StrictEncode + StrictDecode
+{
+}
+
+/// Trait for concrete implementations of seal resolution issues reported by
+/// [`SealResolver`]s during client-side-validation process
+pub trait SealIssue: ValidationLog + std::error::Error {
+    /// Type defining single-use-seals used by the client-side-validated data
+    /// and the seal resolver
+    type SingleUseSeal: SingleUseSeal;
+
+    /// Method returning single-use-seal specific to the reported issue
+    fn seal(&self) -> &Self::SingleUseSeal;
+}
+
+/// Validation failures marker trait indicating that the data had not passed
+/// client-side-validation and must not be accepted by the client. This does not
+/// cover issues related to single-use-seal status, which are covered by
+/// [`SealIssue`] type
+pub trait ValidationFailure: ValidationLog + std::error::Error {}
+
+/// Trait combining different forms of client-side-validation reporting as into
+/// a single type pack
+pub trait ValidationReport {
+    /// Reports on seal resolution issues, for instance produced by failing
+    /// accessing the single-use-seals or its medium or inability to
+    /// determine whether a given seal was closed.
+    type SealIssue: SealIssue;
+
+    /// Internal client-side-validated data inconsistency/invalidity codes and
+    /// reports
+    type Failure: ValidationFailure;
+
+    /// Issues which does not render client-side-validated data invalid, but
+    /// which should be reported to the user anyway
+    type Warning: Clone
+        + Eq
+        + Hash
+        + Debug
+        + Display
+        + StrictEncode
+        + StrictDecode;
+
+    /// Information reports about client-side-validation, which do not affect
+    /// data safety or validity and may not be presented to the user
+    type Info: Clone + Eq + Hash + Debug + Display + StrictEncode + StrictDecode;
+}
+
+/// Client-side-validation status containing all reports from the validation
+/// process
+#[derive(
+    Clone, PartialEq, Eq, Hash, Debug, Default, StrictEncode, StrictDecode,
+)]
+pub struct Status<R>
+where
+    R: ValidationReport,
+{
+    /// List of seal resolver reported issues (see [`SealIssue`] trait for
+    /// details).
+    pub seal_issues: Vec<R::SealIssue>,
+
+    /// Failures generated during client-side-validation.
+    ///
+    /// When the failure happens, the process of client-side-validation is not
+    /// stopped and proceeds to the rest of data items, such that there might
+    /// be a multiple validation failures stored in this array.
+    ///
+    /// Does not include issues from single-use-seal resolution, which are
+    /// stored in [`Status::seal_issues`] and must be handled separately.
+    pub failures: Vec<R::Failure>,
+
+    /// Warnings generated during client-side-validation.
+    ///
+    /// Warnings are issues which does not render client-side-validated data
+    /// invalid, but which should be reported to the user anyway
+    ///
+    /// See also [`ValidationReport::Warning`].
+    pub warnings: Vec<R::Warning>,
+
+    /// Information reports about client-side-validation, which do not affect
+    /// data safety or validity and may not be presented to the user
+    ///
+    /// See also [`ValidationReport::Info`].
+    pub info: Vec<R::Info>,
+}
+
+impl<R> AddAssign for Status<R>
+where
+    R: ValidationReport,
+{
+    fn add_assign(&mut self, rhs: Self) {
+        self.seal_issues.extend(rhs.seal_issues);
+        self.failures.extend(rhs.failures);
+        self.warnings.extend(rhs.warnings);
+        self.info.extend(rhs.info);
+    }
+}
+
+impl<R> FromIterator<R::Failure> for Status<R>
+where
+    R: ValidationReport,
+{
+    fn from_iter<T: IntoIterator<Item = R::Failure>>(iter: T) -> Self {
+        Status {
+            seal_issues: vec![],
+            failures: iter.into_iter().collect(),
+            warnings: vec![],
+            info: vec![],
+        }
+    }
+}
+
+impl<R> Status<R>
+where
+    R: ValidationReport,
+{
+    /// Constructs empty status report
+    pub fn new() -> Self {
+        Status {
+            seal_issues: vec![],
+            failures: vec![],
+            warnings: vec![],
+            info: vec![],
+        }
+    }
+
+    /// Constructs status report from a single failure with the rest of log
+    /// lists set to the empty state.
+    pub fn from_failure(failure: R::Failure) -> Self {
+        Status {
+            seal_issues: vec![],
+            failures: vec![failure],
+            warnings: vec![],
+            info: vec![],
+        }
+    }
+
+    /// Adds a single [`SealIssue`] to the validation report logs
+    pub fn add_seal_issue(&mut self, seal_issue: R::SealIssue) -> &Self {
+        self.seal_issues.push(seal_issue);
+        self
+    }
+
+    /// Adds a single [`ValidationFailure`] to the validation report logs
+    pub fn add_failure(&mut self, failure: R::Failure) -> &Self {
+        self.failures.push(failure);
+        self
+    }
+
+    /// Adds a single warning entry to the validation report logs. See
+    /// [`ValidationReport::Warning`] for more details about warnings.
+    pub fn add_warning(&mut self, warning: R::Warning) -> &Self {
+        self.warnings.push(warning);
+        self
+    }
+
+    /// Adds a single information record to the validation report logs. See
+    /// [`ValidationReport::Info`] for more details about information log
+    /// entries.
+    pub fn add_info(&mut self, info: R::Info) -> &Self {
+        self.info.push(info);
+        self
+    }
+
+    /// Returns validity of the client-side data deduced from the current status
+    /// containing all reported issues.
+    ///
+    /// Client-side data are valid ([`Validity::Valid`] status) only and only if
+    /// the status report contains no validation failures and seal resolution
+    /// issues.
+    ///
+    /// See also [`Validity`] for the details of possible validation statuses.
+    pub fn validity(&self) -> Validity {
+        if !self.failures.is_empty() {
+            Validity::Invalid
+        } else if !self.seal_issues.is_empty() {
+            Validity::SealIssues
+        } else {
+            Validity::Valid
+        }
+    }
+
+    /// Method used to check whether seal resolution issues are resolved, for
+    /// instance allowing to try a different resolver, re-check after restored
+    /// network connectivity or after some time allowing unmined transactions to
+    /// be mined.
+    pub fn resolve_seal_issues<Resolver>(&mut self, _resolver: &mut Resolver)
+    where
+        Resolver: SealResolver<
+            <R::SealIssue as SealIssue>::SingleUseSeal,
+            Error = R::SealIssue,
+        >,
+    {
+        todo!()
+    }
+}
+
+/// This simple trait MUST be used by all top-level data structures implementing
+/// client-side validation paradigm. The core concept of this paradigm is that a
+/// client must have a complete and uniform set of data, which can be
+/// represented or accessed through a single structure; and MUST be able to
+/// deterministically validate this set giving an external validation function,
+/// that is able to provide validator with
+pub trait ClientSideValidate<'a>: ClientData
+where
+    Self::ValidationItem: 'a,
+{
+    /// Data type for data sub-entries contained withing the current
+    /// client-side-validated data item.
+    ///
+    /// If the client-side-validated data contain different types of internal
+    /// entries, this may be a special enum type with a per-data-type variant.
+    ///
+    /// If the data do not contain internal data, set this type to `()`.
+    type ValidationItem: ClientData<ValidationReport = Self::ValidationReport>;
+
+    /// Iterator over the list of specific validation items.
+    ///
+    /// If the client-side-validated data contain different types of internal
+    /// entries, this may be a special enum type with a per-data-type variant.
+    type ValidationIter: Iterator<Item = &'a Self::ValidationItem>;
+
+    /// The mein method performing client-side-validation for the whole block of
+    /// client-side-validated data.
+    ///
+    /// The default implementation of the trait iterates over
+    /// client-side-validated data hierarchy using iterator returned by
+    /// [`ClientSideValidate::validation_iter`] and for each of the items
+    /// - validates internal data consistency with
+    ///   [`ClientData::validate_internal_consistency`] method,
+    /// - validates single-use-seal for the item returned by
+    ///   [`ClientData::single_use_seal`] method using the provided `resolver`
+    ///   object and a context returned by [`ClientSideValidate::context_for`],
+    /// adding reported issues to the [`Status`] log returned by the function.
+    ///
+    /// The function should not fail on any validation failures and run the
+    /// whole validation process up to the end, accumulating all failures and
+    /// reported issues withing [`Status`] object, returned by the function at
+    /// the end.
+    fn client_side_validate<Resolver>(
+        &'a self,
+        resolver: &'a mut Resolver,
+    ) -> Status<Self::ValidationReport>
+    where
+        Resolver: SealResolver<
+            <<<Self as ClientData>::ValidationReport as ValidationReport>::SealIssue as SealIssue>::SingleUseSeal,
+            Error = <<Self as ClientData>::ValidationReport as ValidationReport>::SealIssue,
+        >,
+    {
+        let mut status = Status::new();
+
+        status += self.validate_internal_consistency();
+        for item in self.validation_iter() {
+            if let Some(seal) = item.single_use_seal() {
+                let _ = resolver
+                    .resolve_trust(&seal)
+                    .map_err(|issue| status.add_seal_issue(issue));
+            }
+            status += item.validate_internal_consistency();
+        }
+
+        status
+    }
+
+    /// Returns iterator over hierarchy of individual data items inside
+    /// client-side-validation data.
+    fn validation_iter(&'a self) -> Self::ValidationIter;
+}
+
+/// Marker trait for client-side-validation data at any level of data hierarchy.
+pub trait ClientData {
+    /// Data type that stores validation report configuration for the validation
+    /// [`Status`] object.
+    ///
+    /// This type defines also a type of single-use-seals via
+    /// [`ValidationReports::SealIssue::SingleUseSeal`].
+    type ValidationReport: ValidationReport;
+
+    /// Method returning single-use-seal reference which corresponds to the
+    /// current piece of client-side-validated data. Should return `None` if the
+    /// client-side-validation data does not associated with any single-use-seal
+    /// at this level of data hierarchy.
+    fn single_use_seal(
+        &self,
+    ) -> Option<
+        &<<Self::ValidationReport as ValidationReport>::SealIssue as SealIssue>::SingleUseSeal,
+    >;
+
+    /// Validates internal consistency of the current client-side-validated data
+    /// item. Must not validate any single-use-seals or commitments against
+    /// external commitment mediums.
+    ///
+    /// The method should not iterate over internal data items and go deeper
+    /// inside the data hierarchy and must validate only data related to the
+    /// single current item. The iteration is performed at higher levels,
+    /// normally as a part of [`ClientSideValidate::client_side_validate`]
+    /// method logic.
+    fn validate_internal_consistency(&self) -> Status<Self::ValidationReport>;
+}
+
+/// Seal resolver validates seal to have `closed` status, or reports
+/// [`SealResolver::Error`] otherwise, if the seal does not have a determined
+/// status or there was a error accessing seal commitment medium. The reported
+/// error does not necessary implies that the seal is not closed and the final
+/// decision about seal status must be solved at upper protocol levels or by a
+/// informed user action.
+///
+/// Seal resolver for a given client data type MUST work with a single type
+/// of [`SealResolver::Context`], defined by an associated type. Seal
+/// resolution MUST always produce a singular success type (defined by `()`) or
+/// fail with a well-defined type of [`SealResolver::Error`].
+///
+/// Seal resolver may have an internal state (represented by `self` reference)
+/// and it does not require to produce a deterministic result for the same
+/// given data piece and context: the seal resolver may depend on previous
+/// operation history and depend on type and other external parameters.
+pub trait SealResolver<Seal: SingleUseSeal> {
+    /// Error type returned by [`SealResolver::resolve_trust`], which should
+    /// cover both errors in accessing single-use-seal medium (like network
+    /// connectivity) or evidences of the facts the seal was not (yet) closed.
+    type Error: SealIssue<SingleUseSeal = Seal>;
+
+    /// Resolves trust to the provided single-use-seal giving the context object
+    /// of [`SealResolver::Context`] type, which may store single-use-seal
+    /// medium access configuration and parameters defining seal status.
+    ///
+    /// The method mutates resolver such that it can be able to store cached
+    /// data from a single-use-seal medium.
+    ///
+    /// Method must fail on both errors in accessing single-use-seal medium
+    /// (like network connectivity) usually defined by [`SealMediumError`] or if
+    /// the seal is not (yet) closed.
+    fn resolve_trust(&mut self, seal: &Seal) -> Result<(), Self::Error>;
+}
+
+#[cfg(test)]
+mod test {
+    //! Tests use emulation of a simple client-side-validated state, consisting
+    //! of an array of data items, each of which has a name bound to a certain
+    //! bitcoin single-use-seal.
+
+    use super::*;
+    use crate::single_use_seals::SealMedium;
+
+    #[test]
+    fn test() {
+        #[derive(
+            Clone,
+            PartialEq,
+            Eq,
+            Hash,
+            Debug,
+            Default,
+            StrictEncode,
+            StrictDecode,
+        )]
+        struct Seal {}
+
+        impl SingleUseSeal for Seal {
+            type Witness = ();
+            type Definition = Self;
+            type Error = Issue;
+
+            fn close(
+                &self,
+                _over: impl AsRef<[u8]>,
+            ) -> Result<Self::Witness, Self::Error> {
+                Ok(())
+            }
+
+            fn verify(
+                &self,
+                _msg: impl AsRef<[u8]>,
+                _witness: &Self::Witness,
+                _medium: &impl SealMedium<Self>,
+            ) -> Result<bool, Self::Error>
+            where
+                Self: Sized,
+            {
+                Ok(true)
+            }
+        }
+
+        #[derive(
+            Clone, PartialEq, Eq, Hash, Debug, StrictEncode, StrictDecode,
+        )]
+        struct Report {}
+
+        #[derive(
+            Clone, PartialEq, Eq, Hash, Debug, StrictEncode, StrictDecode,
+        )]
+        struct Issue {
+            seal: Seal,
+        }
+        impl Display for Issue {
+            fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+                f.write_str("")
+            }
+        }
+        impl std::error::Error for Issue {}
+
+        impl ValidationLog for Issue {}
+
+        impl ValidationFailure for Issue {}
+
+        impl SealIssue for Issue {
+            type SingleUseSeal = Seal;
+
+            fn seal(&self) -> &Self::SingleUseSeal {
+                &self.seal
+            }
+        }
+
+        impl ValidationReport for Report {
+            type SealIssue = Issue;
+            type Failure = Issue;
+            type Warning = Issue;
+            type Info = Issue;
+        }
+
+        #[derive(Default)]
+        struct Data {
+            seal: Seal,
+        }
+
+        impl ClientData for Data {
+            type ValidationReport = Report;
+
+            fn single_use_seal(&self) -> Option<&Seal> {
+                Some(&self.seal)
+            }
+
+            fn validate_internal_consistency(
+                &self,
+            ) -> Status<Self::ValidationReport> {
+                Status::new()
+            }
+        }
+
+        struct State {
+            pub data: Vec<Data>,
+        }
+
+        impl ClientData for State {
+            type ValidationReport = Report;
+
+            fn single_use_seal(&self) -> Option<&Seal> {
+                None
+            }
+
+            fn validate_internal_consistency(
+                &self,
+            ) -> Status<Self::ValidationReport> {
+                Status::new()
+            }
+        }
+
+        impl<'a> ClientSideValidate<'a> for State {
+            type ValidationItem = Data;
+            type ValidationIter = std::slice::Iter<'a, Data>;
+
+            fn validation_iter(&'a self) -> Self::ValidationIter {
+                self.data.iter()
+            }
+        }
+
+        #[derive(Default)]
+        struct Resolver {}
+
+        impl SealResolver<Seal> for Resolver {
+            type Error = Issue;
+
+            fn resolve_trust(
+                &mut self,
+                _seal: &Seal,
+            ) -> Result<(), Self::Error> {
+                Ok(())
+            }
+        }
+
+        let state = State {
+            data: vec![Data::default()],
+        };
+        let mut resolver = Resolver::default();
+        let status = state.client_side_validate(&mut resolver);
+        assert_eq!(status, Status::new());
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 8592e204..849a476f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,23 +16,31 @@
 #![recursion_limit = "256"]
 #![deny(dead_code, missing_docs, warnings)]
 
-//! Primitives module defines core strict interfaces from informational LNPBP
-//! standards specifying secure and robust practices for function calls
-//! used in main LNP/BP development paradigms:
-//! * Cryptographic commitments and verification
-//! * Single-use seals
+//! The LNP/BP client-side-validation foundation libraries implementing LNPBP
+//! specifications & standards (LNPBP-4, 7, 8, 9, 42, 81).
+//!
+//! Defines core interfaces from LNPBP standards specifying secure and robust
+//! practices via well-formet APIs. Consists of the following main components:
 //! * Client-side validation
+//! * Cryptographic commitments and verification
+//! * Single-use-seals
 //! * Strict binary data serialization used by client-side validation
 //!
 //! The goal of this module is to maximally reduce the probability of errors and
 //! mistakes within particular implementations of this paradigms by
-//! standartizing typical workflow processes in a form of interfaces that
-//! will be nearly impossible to use in the wrong form.
+//! standardizing typical workflow processes in a form of interfaces that
+//! will be nearly impossible to use in a wrong way.
 
 pub extern crate commit_verify;
 pub extern crate single_use_seals;
 pub extern crate strict_encoding;
 
+mod api;
+
+pub use api::{
+    ClientData, ClientSideValidate, SealIssue, SealResolver, Status,
+    ValidationFailure, ValidationLog, ValidationReport, Validity,
+};
 pub use commit_verify::commit_encode;
 pub use commit_verify::merkle;
 pub use commit_verify::multi_commit;
diff --git a/src/trust_resolvers.rs b/src/trust_resolvers.rs
deleted file mode 100644
index 71895527..00000000
--- a/src/trust_resolvers.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-// LNP/BP client-side-validation foundation libraries implementing LNPBP
-// specifications & standards (LNPBP-4, 7, 8, 9, 42, 81)
-//
-// Written in 2019-2021 by
-//     Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
-//
-// To the extent possible under law, the author(s) have dedicated all
-// copyright and related and neighboring rights to this software to
-// the public domain worldwide. This software is distributed without
-// any warranty.
-//
-// You should have received a copy of the Apache 2.0 License along with this
-// software. If not, see <https://opensource.org/licenses/Apache-2.0>.
-
-//! This is a planned API for v0.5.0 that will help structuring RGB validation
-//! into a more formal process
-
-// TODO: #11 Finalize trust resolvers API
-
-/// This simple trait MUST be used by all parties implementing client-side
-/// validation paradigm. The core concept of this paradigm is that a client
-/// must have a complete and uniform set of data, which can be represented
-/// or accessed through a single structure; and MUST be able to
-/// deterministically validate this set giving an external validation function,
-/// that is able to provide validator with
-pub trait ClientSideValidate<Resolver>
-where
-    Resolver: TrustResolver,
-{
-    type ClientData: ClientData;
-    type ValidationError: FromTrustProblem<Resolver>
-        + FromInternalInconsistency<Resolver>;
-
-    fn new() -> Self;
-
-    fn client_side_validate(
-        client_data: Self::ClientData,
-        trust_resolver: Resolver,
-    ) -> Result<(), Self::ValidationError> {
-        let validator = Self::new();
-        client_data.validate_internal_consistency()?;
-        client_data.validation_iter().try_for_each(|item| {
-            trust_resolver
-                .resolve_trust(item, validator.get_context_for_atom(item))?;
-            item.client_side_validate()
-        })
-    }
-
-    fn get_context_for_item<Ctx>(
-        &self,
-        data_item: Self::ClientData::ValidationItem,
-    ) -> Ctx;
-}
-
-pub trait ClientData {
-    type ValidationItem: ClientData;
-}
-
-/// Trust resolver for a given client data type MUST work with a single type
-/// of [`TrustResolver::Context`], defined by an associated type. Trust
-/// resolution MUST always produce a singular success type (defined by `()`) or
-/// fail with a well-defined type of [`TrustResolver::TrustProblem`].
-///
-/// Trust resolver may have an internal state (represented by `self` reference)
-/// and it does not require to produce a deterministic result for the same
-/// given data piece and context: the trust resolver may depend on previous
-/// operation history and depend on type and other external parameters.
-pub trait TrustResolver<T: ClientData> {
-    type TrustProblem: std::error::Error;
-    type Context;
-    fn resolve_trust(
-        &self,
-        data_piece: &T,
-        context: &Self::Context,
-    ) -> Result<(), Self::TrustProblem>;
-}
diff --git a/strict_encoding/Cargo.toml b/strict_encoding/Cargo.toml
index 8aa12dde..2fdaeb89 100644
--- a/strict_encoding/Cargo.toml
+++ b/strict_encoding/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "strict_encoding"
-version = "1.5.0-beta.1"
+version = "1.5.0"
 license = "Apache-2.0"
 authors = ["Dr. Maxim Orlovsky <orlovsky@pandoracore.com>"]
 description = "Strict encoding: deterministic binary serialization for networking & client-side validation"
diff --git a/strict_encoding/derive/Cargo.toml b/strict_encoding/derive/Cargo.toml
index b1a95455..4c7c55ff 100644
--- a/strict_encoding/derive/Cargo.toml
+++ b/strict_encoding/derive/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "strict_encoding_derive"
-version = "1.1.1"
+version = "1.5.0"
 license = "Apache-2.0"
 authors = ["Dr. Maxim Orlovsky <orlovsky@pandoracore.com>"]
 description = "Strict encoding derivation macros"


Test patch: