diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..33b79214 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "cargo" + directory: "/invoice" + schedule: + interval: "weekly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 24874bf7..e2a308d9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,10 +1,16 @@ -name: Build +name: Build Check on: push: - branches: [ master ] + branches: + - master + - develop + - 'v[0-9]+.[0-9]+' pull_request: - branches: [ master ] + branches: + - master + - develop + - 'v[0-9]+.[0-9]+' env: CARGO_TERM_COLOR: always @@ -13,13 +19,14 @@ jobs: default: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + # NOTE: Dont use nix here, everything should be based on the ubuntu-latest - name: Install rust stable uses: actions-rs/toolchain@v1 with: toolchain: stable override: true - - name: Default build + - name: Latest Ubuntu build check uses: actions-rs/cargo@v1 with: command: check @@ -33,22 +40,13 @@ jobs: - fs - serde steps: - - uses: actions/checkout@v2 - - name: Install rust stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - name: Feature ${{ matrix.feature }} - uses: actions-rs/cargo@v1 - with: - command: check - args: --no-default-features --features=${{ matrix.feature }} - - name: Defaults + ${{ matrix.feature }} - uses: actions-rs/cargo@v1 - with: - command: check - args: --features=${{ matrix.feature }} + - uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v26 + - name: Check feature ${{ matrix.feature }} only + run: nix develop .#stable -c cargo check --no-default-features --features=${{ matrix.feature }} + - name: Check feature ${{ matrix.feature }} with defaults + run: nix develop .#stable -c cargo check --features=${{ matrix.feature }} platforms: runs-on: ${{ matrix.os }} strategy: @@ -56,13 +54,14 @@ jobs: matrix: os: [ ubuntu-20.04, ubuntu-22.04, macos-12, macos-13, windows-2019, windows-2022 ] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + # NOTE: Dont use nix in platform checks everything should based on the host system - name: Install rust stable uses: actions-rs/toolchain@v1 with: toolchain: stable override: true - - name: Build with all features + - name: Build check with all features uses: actions-rs/cargo@v1 with: command: check @@ -72,16 +71,11 @@ jobs: strategy: fail-fast: false matrix: - toolchain: [ nightly, beta, stable, 1.75.0 ] + toolchain: [ nightly, beta, stable, msrv ] steps: - - uses: actions/checkout@v2 - - name: Install rust ${{ matrix.toolchain }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} - override: true - - name: All features - uses: actions-rs/cargo@v1 - with: - command: check - args: --workspace --all-targets --all-features + - name: checkout + uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v26 + - name: Check Crates + run: nix develop ".#${{ matrix.toolchain }}" -c cargo check --workspace --all-targets --all-features diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 20773195..4a23e91b 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -2,9 +2,15 @@ name: Codecov on: push: - branches: [ master ] + branches: + - master + - develop + - 'v[0-9]+.[0-9]+' pull_request: - branches: [ master ] + branches: + - master + - develop + - 'v[0-9]+.[0-9]+' env: CARGO_TERM_COLOR: always @@ -14,38 +20,22 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: rustfmt, llvm-tools-preview + - name: Install Nix + uses: cachix/install-nix-action@v26 - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --release - env: - CARGO_INCREMENTAL: "0" - RUSTFLAGS: "-Cinstrument-coverage" - RUSTDOCFLAGS: "-Cinstrument-coverage" + run: nix develop .#codecov -c cargo build --release - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: --all-features --no-fail-fast - env: - CARGO_INCREMENTAL: "0" - RUSTFLAGS: "-Cinstrument-coverage" - RUSTDOCFLAGS: "-Cinstrument-coverage" + run: nix develop .#codecov -c cargo test --all-features --no-fail-fast --tests - name: Install grcov - run: if [[ ! -e ~/.cargo/bin/grcov ]]; then cargo install grcov; fi + run: nix develop .#codecov -c cargo install grcov - name: Generate coverage - run: grcov . --binary-path target/debug/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../**' --ignore '/*' -o coverage.lcov + run: nix develop .#codecov -c grcov . --binary-path target/debug/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../**' --ignore '/*' -o coverage.lcov - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: ./coverage.lcov flags: rust - fail_ci_if_error: true + # TODO: set true when CODECOV_TOKEN is set + fail_ci_if_error: false + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3f51bde0..7ca82979 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,9 +2,15 @@ name: Lints on: push: - branches: [ master ] + branches: + - master + - develop + - 'v[0-9]+.[0-9]+' pull_request: - branches: [ master ] + branches: + - master + - develop + - 'v[0-9]+.[0-9]+' env: CARGO_TERM_COLOR: always @@ -13,45 +19,24 @@ jobs: fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install rustc nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - components: rustfmt - - uses: actions-rs/cargo@v1 - name: Formatting - with: - command: fmt - args: --all -- --check + - uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v26 + - name: Formatting + run: nix develop .#nightly -c cargo fmt --all -- --check clippy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install rustc stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - components: clippy - - uses: actions-rs/cargo@v1 - name: Clippy - with: - command: clippy - args: --workspace --all-features --all-targets -- -D warnings + - uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v26 + - name: Clippy + run: nix develop .#stable -c cargo clippy --workspace --all-features --all-targets -- -D warnings doc: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install rustc nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - components: rust-docs - - uses: actions-rs/cargo@v1 - name: Doc - with: - command: doc - args: --workspace --all-features + - uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v26 + - name: Doc + run: nix develop .#nightly -c cargo doc --workspace --all-features diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f40b2cd0..1dae2740 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,9 +2,15 @@ name: Tests on: push: - branches: [ master ] + branches: + - master + - develop + - 'v[0-9]+.[0-9]+' pull_request: - branches: [ master ] + branches: + - master + - develop + - 'v[0-9]+.[0-9]+' env: CARGO_TERM_COLOR: always @@ -13,29 +19,16 @@ jobs: testing: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install latest stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true + - uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v26 - name: Build & test - uses: actions-rs/cargo@v1 - with: - command: test - args: --workspace --all-features --no-fail-fast + run: nix develop .#stable -c cargo test --workspace --all-features --no-fail-fast --tests wasm-testing: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install rust nightly - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - - uses: Swatinem/rust-cache@v2 - - uses: jetli/wasm-pack-action@v0.3.0 - - name: Add wasm32 target - run: rustup target add wasm32-unknown-unknown + - uses: actions/checkout@v4 + - name: Install Nix + uses: cachix/install-nix-action@v26 - name: Test in headless Chrome - run: wasm-pack test --headless --chrome + run: nix develop .#wasm -c wasm-pack test --headless --chrome diff --git a/.gitignore b/.gitignore index e8ab911e..2f2af66b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ *.swp /dep_test +result +default_*.profraw +coverage.lcov diff --git a/README.md b/README.md index cd72d957..7e26ed0b 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ by default. ### MSRV -Minimum supported rust compiler version (MSRV): 1.66, rust 2021 edition. +Minimum supported rust compiler version (MSRV) is shown in `rust-version` of `Cargo.toml`. ## Contributing diff --git a/asset/armored_contract.default b/asset/armored_contract.default new file mode 100644 index 00000000..106d24d0 --- /dev/null +++ b/asset/armored_contract.default @@ -0,0 +1,13 @@ +-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:poAMvm9j-NdapxqA-MJ@5dwP-d@IIt2A-T@5OiXE-Tl54Yew#guide-campus-arctic +Version: 2 +Type: contract +Contract: rgb:qm7P@06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe +Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 + +0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 +0000000000000 + +-----END RGB CONSIGNMENT----- diff --git a/asset/armored_kit.default b/asset/armored_kit.default new file mode 100644 index 00000000..3cd1403e --- /dev/null +++ b/asset/armored_kit.default @@ -0,0 +1,9 @@ +-----BEGIN RGB KIT----- +Id: rgb:kit:e1jW6Rgc-2$JzXDg-XmR8XRJ-v@q$Dzf-yImkPjD-t8EjfvI +Version: 2 +Type-System: sts:8Vb$sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel +Check-SHA256: 5563cc1568e244183804e0db3cec6ff9bf577f4a403924096177bf4a586160da + +0ssI2000000000 + +-----END RGB KIT----- diff --git a/asset/armored_transfer.default b/asset/armored_transfer.default new file mode 100644 index 00000000..106d24d0 --- /dev/null +++ b/asset/armored_transfer.default @@ -0,0 +1,13 @@ +-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:poAMvm9j-NdapxqA-MJ@5dwP-d@IIt2A-T@5OiXE-Tl54Yew#guide-campus-arctic +Version: 2 +Type: contract +Contract: rgb:qm7P@06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe +Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 + +0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 +0000000000000 + +-----END RGB CONSIGNMENT----- diff --git a/asset/contract.default b/asset/contract.default new file mode 100644 index 00000000..33e2bd71 Binary files /dev/null and b/asset/contract.default differ diff --git a/asset/kit.default b/asset/kit.default new file mode 100644 index 00000000..67d729f7 Binary files /dev/null and b/asset/kit.default differ diff --git a/asset/transfer.default b/asset/transfer.default new file mode 100644 index 00000000..2ae06b2d Binary files /dev/null and b/asset/transfer.default differ diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..6894ac04 --- /dev/null +++ b/flake.lock @@ -0,0 +1,85 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1712439257, + "narHash": "sha256-aSpiNepFOMk9932HOax0XwNxbA38GOUVOiXfUVPOrck=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ff0dbd94265ac470dda06a657d5fe49de93b4599", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712542394, + "narHash": "sha256-UZebDBECRSrJqw4K+LxZ6qFdYnScu6q1XCwqtsu1cas=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ece8bdb3c3b58def25f204b9a1261dee55d7c9c0", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..97a9cc3d --- /dev/null +++ b/flake.nix @@ -0,0 +1,80 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + rust-overlay = { + url = "github:oxalica/rust-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-utils.follows = "flake-utils"; + }; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, rust-overlay, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { + inherit system overlays; + }; + + cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml); + + nightlyWithWasm = pkgs.rust-bin.nightly.latest.default.override { + extensions = [ ]; + targets = [ "wasm32-unknown-unknown" ]; + }; + + stableWithLlvm = pkgs.rust-bin.nightly.latest.default.override { + extensions = [ "rustfmt" "llvm-tools-preview" ]; + targets = [ ]; + }; + in + with pkgs; + { + devShells = rec { + default = msrv; + + msrv = mkShell { + buildInputs = [ + rust-bin.stable."${cargoToml.workspace.package."rust-version"}".default + ]; + }; + + stable = mkShell { + buildInputs = [ + rust-bin.stable.latest.default + ]; + }; + + beta = mkShell { + buildInputs = [ + rust-bin.beta.latest.default + ]; + }; + + nightly = mkShell { + buildInputs = [ + rust-bin.nightly.latest.default + ]; + }; + + wasm = mkShell { + buildInputs = [ + nightlyWithWasm + chromedriver + wasm-pack + ]; + }; + + codecov = mkShell { + buildInputs = [ + stableWithLlvm + ]; + CARGO_INCREMENTAL = "0"; + RUSTFLAGS = "-Cinstrument-coverage"; + RUSTDOCFLAGS = "-Cinstrument-coverage"; + }; + }; + } + ); +} diff --git a/src/containers/consignment.rs b/src/containers/consignment.rs index 0064ccff..e483fe33 100644 --- a/src/containers/consignment.rs +++ b/src/containers/consignment.rs @@ -137,7 +137,7 @@ impl Deref for ValidConsignment { /// with `endpoints` and process up to the genesis. #[derive(Clone, Debug, Display)] #[display(AsciiArmor::to_ascii_armored_string)] -#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] +#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode, PartialEq)] #[strict_type(lib = LIB_NAME_RGB_STD)] #[cfg_attr( feature = "serde", @@ -377,7 +377,7 @@ impl StrictArmor for Consignment { fn armor_id(&self) -> Self::Id { self.commit_id() } fn armor_headers(&self) -> Vec { - vec![ + let mut headers = vec![ ArmorHeader::new(ASCII_ARMOR_VERSION, format!("{:#}", self.version)), ArmorHeader::new( ASCII_ARMOR_CONSIGNMENT_TYPE, @@ -389,11 +389,175 @@ impl StrictArmor for Consignment { ), ArmorHeader::new(ASCII_ARMOR_CONTRACT, self.contract_id().to_string()), ArmorHeader::new(ASCII_ARMOR_SCHEMA, self.schema.schema_id().to_string()), - ArmorHeader::with( + ]; + if !self.ifaces.is_empty() { + headers.push(ArmorHeader::with( ASCII_ARMOR_IFACE, self.ifaces.keys().map(|iface| iface.name.to_string()), - ), - ArmorHeader::with(ASCII_ARMOR_TERMINAL, self.terminals.keys().map(BundleId::to_string)), - ] + )); + } + if !self.terminals.is_empty() { + headers.push(ArmorHeader::with( + ASCII_ARMOR_TERMINAL, + self.terminals.keys().map(BundleId::to_string), + )); + } + headers + } +} + +impl FromStr for Consignment { + type Err = armor::StrictArmorError; + fn from_str(s: &str) -> Result { Self::from_ascii_armored_str(s) } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn contract_str_round_trip() { + let contract = Contract::from_str(include_str!("../../asset/armored_contract.default")) + .expect("contract from str should work"); + assert_eq!( + contract.to_string(), + include_str!("../../asset/armored_contract.default"), + "contract string round trip fails" + ); + } + + #[test] + fn error_contract_strs() { + assert!( + Contract::from_str( + r#"-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:poAMvm9j-NdapxqA-MJ@5dwP-d@IIt2A-T@5OiXE-Tl54Yew#guide-campus-arctic +Version: 2 +Type: contract +Contract: rgb:qm7P@06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe +Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 + +0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 +0000000000000 + +-----END RGB CONSIGNMENT-----"# + ) + .is_ok() + ); + + // Wrong Id + assert!( + Contract::from_str( + r#"-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:aaaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa#guide-campus-arctic +Version: 2 +Type: contract +Contract: rgb:qm7P@06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe +Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 + +0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 +0000000000000 + +-----END RGB CONSIGNMENT-----"# + ) + .is_err() + ); + + // Wrong checksum + assert!( + Contract::from_str( + r#"-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:poAMvm9j-NdapxqA-MJ@5dwP-d@IIt2A-T@5OiXE-Tl54Yew#guide-campus-arctic +Version: 2 +Type: contract +Contract: rgb:qm7P@06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe +Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 +0000000000000 + +-----END RGB CONSIGNMENT-----"# + ) + .is_err() + ); + } + + #[test] + fn transfer_str_round_trip() { + let transfer = Transfer::from_str(include_str!("../../asset/armored_transfer.default")) + .expect("transfer from str should work"); + assert_eq!( + transfer.to_string(), + include_str!("../../asset/armored_transfer.default"), + "transfer string round trip fails" + ); + } + + #[test] + fn error_transfer_strs() { + assert!( + Transfer::from_str( + r#"-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:poAMvm9j-NdapxqA-MJ@5dwP-d@IIt2A-T@5OiXE-Tl54Yew#guide-campus-arctic +Version: 2 +Type: contract +Contract: rgb:qm7P@06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe +Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 + +0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 +0000000000000 + +-----END RGB CONSIGNMENT-----"# + ) + .is_ok() + ); + + // Wrong Id + assert!( + Transfer::from_str( + r#"-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:aaaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa-aaaaaaa#guide-campus-arctic +Version: 2 +Type: contract +Contract: rgb:qm7P@06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe +Check-SHA256: 181748dae0c83cbb44f6ccfdaddf6faca0bc4122a9f35fef47bab9aea023e4a1 + +0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 +0000000000000 + +-----END RGB CONSIGNMENT-----"# + ) + .is_err() + ); + + // Wrong checksum + assert!( + Transfer::from_str( + r#"-----BEGIN RGB CONSIGNMENT----- +Id: rgb:csg:poAMvm9j-NdapxqA-MJ@5dwP-d@IIt2A-T@5OiXE-Tl54Yew#guide-campus-arctic +Version: 2 +Type: contract +Contract: rgb:qm7P@06T-uuBQT56-ovwOLzx-9Gka7Nb-84Nwo8g-blLb8kw +Schema: rgb:sch:CyqM42yAdM1moWyNZPQedAYt73BM$k9z$dKLUXY1voA#cello-global-deluxe +Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +0ssI2000000000000000000000000000000000000000000000000000000D0CRI`I$>^aZh38Qb#nj! +0000000000000000000000d59ZDjxe00000000dDb8~4rVQz13d2MfXa{vGU00000000000000000000 +0000000000000 + +-----END RGB CONSIGNMENT-----"# + ) + .is_err() + ); } } diff --git a/src/containers/file.rs b/src/containers/file.rs index 479364b5..78d02ef3 100644 --- a/src/containers/file.rs +++ b/src/containers/file.rs @@ -193,3 +193,262 @@ impl Display for UniversalFile { } } } + +#[cfg(test)] +mod test { + use std::fs::OpenOptions; + use std::str::FromStr; + + use super::*; + static DEFAULT_KIT_PATH: &str = "asset/kit.default"; + #[cfg(feature = "fs")] + static ARMORED_KIT_PATH: &str = "asset/armored_kit.default"; + + static DEFAULT_CONTRACT_PATH: &str = "asset/contract.default"; + #[cfg(feature = "fs")] + static ARMORED_CONTRACT_PATH: &str = "asset/armored_contract.default"; + + static DEFAULT_TRANSFER_PATH: &str = "asset/transfer.default"; + #[cfg(feature = "fs")] + static ARMORED_TRANSFER_PATH: &str = "asset/armored_transfer.default"; + + #[test] + fn kit_save_load_round_trip() { + let mut kit_file = OpenOptions::new() + .read(true) + .open(DEFAULT_KIT_PATH) + .unwrap(); + let kit = Kit::load(kit_file).expect("fail to load kit.default"); + let default_kit = Kit::default(); + assert_eq!(kit, default_kit, "kit default is not same as before"); + + kit_file = OpenOptions::new() + .write(true) + .open(DEFAULT_KIT_PATH) + .unwrap(); + default_kit.save(kit_file).expect("fail to export kit"); + + kit_file = OpenOptions::new() + .read(true) + .open(DEFAULT_KIT_PATH) + .unwrap(); + let kit = Kit::load(kit_file).expect("fail to load kit.default"); + assert_eq!(kit, default_kit, "kit roudtrip does not work"); + } + + #[cfg(feature = "fs")] + #[test] + fn armored_kit_save_load_round_trip() { + let kit_file = OpenOptions::new() + .read(true) + .open(DEFAULT_KIT_PATH) + .unwrap(); + let kit = Kit::load(kit_file).expect("fail to load kit.default"); + let unarmored_kit = + Kit::load_armored(ARMORED_KIT_PATH).expect("fail to export armored kit"); + assert_eq!(kit, unarmored_kit, "kit unarmored is not the same"); + + let default_kit = Kit::default(); + default_kit + .save_armored(ARMORED_KIT_PATH) + .expect("fail to save armored kit"); + let kit = Kit::load_armored(ARMORED_KIT_PATH).expect("fail to export armored kit"); + assert_eq!(kit, default_kit, "armored kit roudtrip does not work"); + } + + // A contract with almost default fields + fn almost_default_contract() -> Contract { + Contract { + version: Default::default(), + transfer: Default::default(), + terminals: Default::default(), + genesis: rgb::Genesis { + ffv: Default::default(), + schema_id: rgb::SchemaId::from_str( + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#distant-history-exotic", + ) + .unwrap(), + flags: Default::default(), + timestamp: Default::default(), + issuer: Default::default(), + testnet: Default::default(), + alt_layers1: Default::default(), + asset_tags: Default::default(), + metadata: Default::default(), + globals: Default::default(), + assignments: Default::default(), + valencies: Default::default(), + validator: Default::default(), + }, + extensions: Default::default(), + bundles: Default::default(), + schema: rgb::Schema { + ffv: Default::default(), + flags: Default::default(), + name: strict_encoding::TypeName::from_str("Name").unwrap(), + timestamp: Default::default(), + developer: Default::default(), + meta_types: Default::default(), + global_types: Default::default(), + owned_types: Default::default(), + valency_types: Default::default(), + genesis: Default::default(), + extensions: Default::default(), + transitions: Default::default(), + reserved: Default::default(), + }, + ifaces: Default::default(), + supplements: Default::default(), + types: Default::default(), + scripts: Default::default(), + attachments: Default::default(), + signatures: Default::default(), + } + } + + #[test] + fn contract_save_load_round_trip() { + let mut contract_file = OpenOptions::new() + .read(true) + .open(DEFAULT_CONTRACT_PATH) + .unwrap(); + let contract = Contract::load(contract_file).expect("fail to load contract.default"); + + let default_contract = almost_default_contract(); + assert_eq!(&contract, &default_contract, "contract default is not same as before"); + + contract_file = OpenOptions::new() + .write(true) + .open(DEFAULT_CONTRACT_PATH) + .unwrap(); + default_contract + .save(contract_file) + .expect("fail to export contract"); + + contract_file = OpenOptions::new() + .read(true) + .open(DEFAULT_CONTRACT_PATH) + .unwrap(); + let contract = Contract::load(contract_file).expect("fail to load contract.default"); + assert_eq!(&contract, &default_contract, "contract roudtrip does not work"); + } + + #[cfg(feature = "fs")] + #[test] + fn armored_contract_save_load_round_trip() { + let contract_file = OpenOptions::new() + .read(true) + .open(DEFAULT_CONTRACT_PATH) + .unwrap(); + let contract = Contract::load(contract_file).expect("fail to load contract.default"); + let unarmored_contract = + Contract::load_armored(ARMORED_CONTRACT_PATH).expect("fail to export armored contract"); + assert_eq!(contract, unarmored_contract, "contract unarmored is not the same"); + + let default_contract = almost_default_contract(); + default_contract + .save_armored(ARMORED_CONTRACT_PATH) + .expect("fail to save armored contract"); + let contract = + Contract::load_armored(ARMORED_CONTRACT_PATH).expect("fail to export armored contract"); + assert_eq!(contract, default_contract, "armored contract roudtrip does not work"); + } + + // A transfer with almost default fields + fn almost_default_transfer() -> Transfer { + Transfer { + version: Default::default(), + transfer: Default::default(), + terminals: Default::default(), + genesis: rgb::Genesis { + ffv: Default::default(), + schema_id: rgb::SchemaId::from_str( + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA#distant-history-exotic", + ) + .unwrap(), + flags: Default::default(), + timestamp: Default::default(), + issuer: Default::default(), + testnet: Default::default(), + alt_layers1: Default::default(), + asset_tags: Default::default(), + metadata: Default::default(), + globals: Default::default(), + assignments: Default::default(), + valencies: Default::default(), + validator: Default::default(), + }, + extensions: Default::default(), + bundles: Default::default(), + schema: rgb::Schema { + ffv: Default::default(), + flags: Default::default(), + name: strict_encoding::TypeName::from_str("Name").unwrap(), + timestamp: Default::default(), + developer: Default::default(), + meta_types: Default::default(), + global_types: Default::default(), + owned_types: Default::default(), + valency_types: Default::default(), + genesis: Default::default(), + extensions: Default::default(), + transitions: Default::default(), + reserved: Default::default(), + }, + ifaces: Default::default(), + supplements: Default::default(), + types: Default::default(), + scripts: Default::default(), + attachments: Default::default(), + signatures: Default::default(), + } + } + + #[test] + fn transfer_save_load_round_trip() { + let mut transfer_file = OpenOptions::new() + .read(true) + .open(DEFAULT_TRANSFER_PATH) + .unwrap(); + let transfer = Transfer::load(transfer_file).expect("fail to load transfer.default"); + + let default_transfer = almost_default_transfer(); + assert_eq!(&transfer, &default_transfer, "transfer default is not same as before"); + + transfer_file = OpenOptions::new() + .write(true) + .open(DEFAULT_TRANSFER_PATH) + .unwrap(); + default_transfer + .save(transfer_file) + .expect("fail to export transfer"); + + transfer_file = OpenOptions::new() + .read(true) + .open(DEFAULT_TRANSFER_PATH) + .unwrap(); + let transfer = Transfer::load(transfer_file).expect("fail to load transfer.default"); + assert_eq!(&transfer, &default_transfer, "transfer roudtrip does not work"); + } + + #[cfg(feature = "fs")] + #[test] + fn armored_transfer_save_load_round_trip() { + let transfer_file = OpenOptions::new() + .read(true) + .open(DEFAULT_TRANSFER_PATH) + .unwrap(); + let transfer = Transfer::load(transfer_file).expect("fail to load transfer.default"); + let unarmored_transfer = + Transfer::load_armored(ARMORED_TRANSFER_PATH).expect("fail to export armored transfer"); + assert_eq!(transfer, unarmored_transfer, "transfer unarmored is not the same"); + + let default_transfer = almost_default_transfer(); + default_transfer + .save_armored(ARMORED_TRANSFER_PATH) + .expect("fail to save armored transfer"); + let transfer = + Transfer::load_armored(ARMORED_TRANSFER_PATH).expect("fail to export armored transfer"); + assert_eq!(transfer, default_transfer, "armored transfer roudtrip does not work"); + } +} diff --git a/src/containers/kit.rs b/src/containers/kit.rs index 6457123c..ffac4378 100644 --- a/src/containers/kit.rs +++ b/src/containers/kit.rs @@ -110,7 +110,7 @@ impl Deref for ValidKit { fn deref(&self) -> &Self::Target { &self.kit } } -#[derive(Clone, Default, Debug, Display)] +#[derive(Clone, Default, Debug, Display, PartialEq)] #[display(AsciiArmor::to_ascii_armored_string)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = LIB_NAME_RGB_STD)] @@ -263,3 +263,80 @@ impl StrictArmor for Kit { headers } } + +impl FromStr for Kit { + type Err = armor::StrictArmorError; + fn from_str(s: &str) -> Result { Self::from_ascii_armored_str(s) } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn kit_str_round_trip() { + let kit = Kit::from_str(include_str!("../../asset/armored_kit.default")) + .expect("kit from str should work"); + + assert_eq!( + kit.to_string(), + include_str!("../../asset/armored_kit.default"), + "kit string round trip fails" + ); + + assert_eq!( + kit.validate().unwrap().to_string(), + include_str!("../../asset/armored_kit.default"), + "validated kit string round trip fails" + ); + } + + #[test] + fn error_kit_strs() { + assert!( + Kit::from_str( + r#"-----BEGIN RGB KIT----- +Id: rgb:kit:e1jW6Rgc-2$JzXDg-XmR8XRJ-v@q$Dzf-yImkPjD-t8EjfvI +Version: 2 +Type-System: sts:8Vb$sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel +Check-SHA256: 5563cc1568e244183804e0db3cec6ff9bf577f4a403924096177bf4a586160da + +0ssI2000000000 + +-----END RGB KIT-----"# + ) + .is_ok() + ); + + // Wrong Id + assert!( + Kit::from_str( + r#"-----BEGIN RGB KIT----- +Id: rgb:kit:11111111-2222222-XmR8XRJ-v@q$Dzf-yImkPjD-t8EjfvI +Version: 2 +Type-System: sts:8Vb$sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel +Check-SHA256: 5563cc1568e244183804e0db3cec6ff9bf577f4a403924096177bf4a586160da + +0ssI2000000000 + +-----END RGB KIT-----"# + ) + .is_err() + ); + + // wrong checksum + assert!( + Kit::from_str( + r#"-----BEGIN RGB KIT----- +Id: rgb:kit:e1jW6Rgc-2$JzXDg-XmR8XRJ-v@q$Dzf-yImkPjD-t8EjfvI +Version: 2 +Type-System: sts:8Vb$sM1F-5MsQc20-HEixf55-gJR37FM-0zRKfpY-SwIp35w#design-farmer-camel +Check-SHA256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +0ssI2000000000 + +-----END RGB KIT-----"# + ) + .is_err() + ); + } +}