diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 20ea47c249..9fb6521b7b 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -34,6 +34,7 @@ on: - 'concordium-consensus/smart-contracts' - 'concordium-consensus/haskell-lmdb' - 'concordium-node/rustfmt.toml' + - 'plt' pull_request: types: [opened, synchronize, reopened, ready_for_review] @@ -48,17 +49,19 @@ on: - 'concordium-consensus/smart-contracts' - 'concordium-consensus/haskell-lmdb' - 'concordium-node/rustfmt.toml' + - 'plt' workflow_dispatch: # allow manual trigger env: dummy: 17 # change to force cache invalidation CARGO_TERM_COLOR: always # implicitly adds '--color=always' to all cargo commands + RUST_VERSION: 1.86 + GHC_VERSION: 9.10.2 jobs: fourmolu: runs-on: ubuntu-latest - if: ${{ !github.event.pull_request.draft }} steps: - name: Download fourmolu @@ -93,25 +96,17 @@ jobs: rustfmt: runs-on: ubuntu-latest - if: ${{ !github.event.pull_request.draft }} - - strategy: - matrix: - plan: - - rust: "1.82" - steps: - name: Checkout uses: actions/checkout@v2 with: - # token: ${{ secrets.CONCORDIUM_CI }} submodules: recursive - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: ${{ matrix.plan.rust }} + toolchain: ${{ env.RUST_VERSION }} override: true components: rustfmt @@ -127,13 +122,6 @@ jobs: needs: [fourmolu, rustfmt] # Use fixed OS version because we install packages on the system. runs-on: ubuntu-22.04 - if: ${{ !github.event.pull_request.draft }} - - strategy: - matrix: - plan: - - rust: 1.82 - ghc: 9.10.2 steps: - name: Remove unnecessary files @@ -164,7 +152,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: ${{ matrix.plan.rust }} + toolchain: ${{ env.RUST_VERSION }} override: true components: clippy, llvm-tools target: x86_64-pc-windows-gnu @@ -184,10 +172,10 @@ jobs: concordium-base/smart-contracts/lib concordium-node/target collector/target - key: ${{ runner.os }}-${{ env.dummy }}-rust-deps-${{ matrix.plan.rust }}-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }}-${{ hashFiles('concordium-base/rust-src/**/*.rs','concordium-base/smart-contracts/wasm-chain-integration/**/*.rs')}} + key: ${{ runner.os }}-${{ env.dummy }}-rust-deps-${{ env.RUST_VERSION }}-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }}-${{ hashFiles('concordium-base/rust-src/**/*.rs','concordium-base/smart-contracts/wasm-chain-integration/**/*.rs')}} restore-keys: | - ${{ runner.os }}-${{ env.dummy }}-rust-deps-${{ matrix.plan.rust }}-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }} - ${{ runner.os }}-${{ env.dummy }}-rust-deps-${{ matrix.plan.rust }} + ${{ runner.os }}-${{ env.dummy }}-rust-deps-${{ env.RUST_VERSION }}-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }} + ${{ runner.os }}-${{ env.dummy }}-rust-deps-${{ env.RUST_VERSION }} # HASKELL # @@ -208,9 +196,9 @@ jobs: uses: actions/cache@v4 with: path: ~/.stack - key: ${{ runner.os }}-${{ env.dummy }}-stack-global-${{ matrix.plan.ghc }}-${{ hashFiles('**.yaml') }} + key: ${{ runner.os }}-${{ env.dummy }}-stack-global-${{ env.GHC_VERSION }}-${{ hashFiles('**.yaml') }} restore-keys: | - ${{ runner.os }}-${{ env.dummy }}-stack-global-${{ matrix.plan.ghc }} + ${{ runner.os }}-${{ env.dummy }}-stack-global-${{ env.GHC_VERSION }} - name: Cache '.stack-work' uses: actions/cache@v4 with: @@ -220,11 +208,11 @@ jobs: concordium-consensus/.stack-work concordium-consensus/haskell-lmdb/.stack-work - key: ${{ runner.os }}-${{ env.dummy }}-stack-work-${{ matrix.plan.ghc }}-${{ hashFiles('**.yaml') }}-${{ steps.cache-keys.outputs.proto_hash }}-${{ steps.cache-keys.outputs.base_hash }} + key: ${{ runner.os }}-${{ env.dummy }}-stack-work-${{ env.GHC_VERSION }}-${{ hashFiles('**.yaml') }}-${{ steps.cache-keys.outputs.proto_hash }}-${{ steps.cache-keys.outputs.base_hash }} restore-keys: | - ${{ runner.os }}-${{ env.dummy }}-stack-work-${{ matrix.plan.ghc }}-${{ hashFiles('**.yaml') }}-${{ steps.cache-keys.outputs.proto_hash }}- - ${{ runner.os }}-${{ env.dummy }}-stack-work-${{ matrix.plan.ghc }}-${{ hashFiles('**.yaml') }}- - ${{ runner.os }}-${{ env.dummy }}-stack-work-${{ matrix.plan.ghc }} + ${{ runner.os }}-${{ env.dummy }}-stack-work-${{ env.GHC_VERSION }}-${{ hashFiles('**.yaml') }}-${{ steps.cache-keys.outputs.proto_hash }}- + ${{ runner.os }}-${{ env.dummy }}-stack-work-${{ env.GHC_VERSION }}-${{ hashFiles('**.yaml') }}- + ${{ runner.os }}-${{ env.dummy }}-stack-work-${{ env.GHC_VERSION }} # Compile Haskell sources. This must be done before running checks or tests on the Rust sources. - name: Build consensus and run tests diff --git a/.github/workflows/plt-build-test.yaml b/.github/workflows/plt-build-test.yaml new file mode 100644 index 0000000000..a19276f3c5 --- /dev/null +++ b/.github/workflows/plt-build-test.yaml @@ -0,0 +1,51 @@ +name: PLT scheduler build and run tests + +on: + push: + branches: + - main + paths: + - '.github/workflows/plt-build-test.yaml' + - 'concordium-base' + - 'plt' + + pull_request: + paths: + - '.github/workflows/plt-build-test.yaml' + - 'concordium-base' + - 'plt' +env: + CARGO_TERM_COLOR: always # implicitly adds '--color=always' to all cargo commands + +jobs: + rustfmt: + name: Check formatting + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: recursive + - name: Run rustfmt + working-directory: plt + run: | + rustup component add rustfmt + cargo fmt --check + + clippy_test: + name: Run Clippy and tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Clippy + working-directory: plt + run: | + rustup component add clippy + cargo clippy --locked --all-targets --all-features -- -D warnings + - name: Test + working-directory: plt + run: | + cargo test --locked --all-targets --all-features diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f6e4e3f170..a607d3e8ed 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -22,7 +22,7 @@ on: env: UBUNTU_VERSION: '22.04' STATIC_LIBRARIES_IMAGE_TAG: 'rust-1.82_ghc-9.10.2' - RUST_VERSION: '1.82' + RUST_VERSION: '1.86' STACK_VERSION: '3.7.1' FLATBUFFERS_VERSION: '23.5.26' GHC_VERSION: '9.10.2' diff --git a/.gitignore b/.gitignore index 0ccf8a9467..702fc77927 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,6 @@ deps/internal/crypto/build *.ps scripts/static-libs/static-libs-docker/output_dir/ result -**/**/Cargo.lock 0 **/**/0 scripts/genesis-data/genesis_data/** @@ -49,6 +48,7 @@ genesis_data /concordium-consensus/a.out /concordium-consensus/HSdll.dll /concordium-consensus/HSdll.dll.a +/concordium-consensus/lib concordium-node/deps/static-libs .dir-locals.el hie.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ac990d901..e32a5b24ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ the token module state. They are still required by the current token module implementation, and initialization without the parameters set will be rejected, so there are no observable changes to PLT behaviour. - Fixed the `build_catchup_url` in the Ubuntu build release pipeline. +- Added boilerplate code for the upcoming P10. ## 9.0.7 diff --git a/collector/src/bin/collector.rs b/collector/src/bin/collector.rs index 1c36435bc4..23f2f229f7 100644 --- a/collector/src/bin/collector.rs +++ b/collector/src/bin/collector.rs @@ -11,7 +11,11 @@ use tonic::transport::{channel::Channel, ClientTlsConfig}; #[macro_use] extern crate log; -#[allow(clippy::large_enum_variant, clippy::enum_variant_names)] +#[allow( + clippy::large_enum_variant, + clippy::enum_variant_names, + clippy::doc_overindented_list_items +)] mod grpc { mod plt { tonic::include_proto!("concordium.v2.plt"); @@ -208,7 +212,7 @@ async fn main() { } #[allow(clippy::cognitive_complexity)] -async fn collect_data<'a>( +async fn collect_data( node_name: NodeName, grpc_host: String, conf: &ConfigCli, diff --git a/concordium-base b/concordium-base index 73e1dad0e4..d0590c6d67 160000 --- a/concordium-base +++ b/concordium-base @@ -1 +1 @@ -Subproject commit 73e1dad0e423a6830d704d15adf732191a5f73df +Subproject commit d0590c6d67232d50342461075267b280c134daf2 diff --git a/concordium-consensus/Setup.hs b/concordium-consensus/Setup.hs index 640b57dca3..be3cba5cc6 100644 --- a/concordium-consensus/Setup.hs +++ b/concordium-consensus/Setup.hs @@ -4,52 +4,74 @@ import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Setup import Distribution.Simple.Utils import Distribution.System +import Distribution.Verbosity import System.Directory import System.Environment import Data.Maybe -smartContractRoot = "../concordium-base/smart-contracts" +-- | Notify and execute a command, if fails exit with the same exit code. +runCmd :: Verbosity -> String -> IO () +runCmd verbosity cmd = do + notice verbosity $ "Running '" ++ cmd ++ "'" + let command : args = words cmd + rawSystemExit verbosity command $ args -makeRust :: Args -> ConfigFlags -> IO HookedBuildInfo -makeRust args flags = do +-- | Path to Concordium Smart Contract Engine rust crate relative to this file. +smartContractEngineCrateRelative = "../concordium-base/smart-contracts/wasm-chain-integration" + +-- | Path to plt rust workspace relative to this file. +pltWorkspaceRelative = "../plt" + +preConfHook :: Args -> ConfigFlags -> IO HookedBuildInfo +preConfHook args flags = do let verbosity = fromFlag $ configVerbosity flags - rawSystemExit verbosity "mkdir" ["-p", smartContractRoot ++ "/lib"] - -- This way of determining the platform is not ideal. - notice verbosity "Calling 'cargo build'" - rawSystemExit - verbosity - "cargo" - ["build", "--release", "--manifest-path", smartContractRoot ++ "/wasm-chain-integration/Cargo.toml", "--features=enable-ffi"] + -- Convert relative paths into absolute paths. + libraryDestination <- canonicalizePath "./lib" + -- Ensure destination directory exists. + runCmd verbosity $ "mkdir -p " ++ libraryDestination + + -- Build and copy/symlink Concordium Smart contract Engine library. + smartContractEngineCrate <- canonicalizePath smartContractEngineCrateRelative + runCmd verbosity $ "cargo build --release --locked --features=enable-ffi --manifest-path=" ++ smartContractEngineCrate ++ "/Cargo.toml" + case buildOS of + Windows -> do + runCmd verbosity $ "cp -u " ++ smartContractEngineCrate ++ "/target/release/concordium_smart_contract_engine.dll " ++ libraryDestination + OSX -> do + runCmd verbosity $ "ln -s -f " ++ smartContractEngineCrate ++ "/target/release/libconcordium_smart_contract_engine.a " ++ libraryDestination + runCmd verbosity $ "ln -s -f " ++ smartContractEngineCrate ++ "/target/release/libconcordium_smart_contract_engine.dylib " ++ libraryDestination + _ -> do + runCmd verbosity $ "ln -s -f " ++ smartContractEngineCrate ++ "/target/release/libconcordium_smart_contract_engine.a " ++ libraryDestination + runCmd verbosity $ "ln -s -f " ++ smartContractEngineCrate ++ "/target/release/libconcordium_smart_contract_engine.so " ++ libraryDestination + + -- Build and copy/symlink PLT scheduler project + pltWorkspace <- canonicalizePath pltWorkspaceRelative + runCmd verbosity $ "cargo build --release --locked --features ffi -p plt-scheduler --manifest-path=" ++ pltWorkspace ++ "/Cargo.toml" case buildOS of Windows -> do - notice verbosity "Copying concordium_smart_contract_engine library" - rawSystemExit verbosity "cp" ["-u", smartContractRoot ++ "/wasm-chain-integration/target/release/concordium_smart_contract_engine.dll", smartContractRoot ++ "/lib/"] - -- We remove the static library if it exists. Previously, it would have been copied - -- over, but now we want to just link with the dynamic library, so we ensure it is - -- removed. - rawSystemExit verbosity "rm" ["-f", smartContractRoot ++ "/lib/libconcordium_smart_contract_engine.a"] + runCmd verbosity $ "cp -u " ++ pltWorkspace ++ "/target/release/plt_scheduler.dll " ++ libraryDestination + OSX -> do + runCmd verbosity $ "ln -s -f " ++ pltWorkspace ++ "/target/release/libplt_scheduler.a " ++ libraryDestination + runCmd verbosity $ "ln -s -f " ++ pltWorkspace ++ "/target/release/libplt_scheduler.dylib " ++ libraryDestination _ -> do - rawSystemExit verbosity "ln" ["-s", "-f", "../wasm-chain-integration/target/release/libconcordium_smart_contract_engine.a", smartContractRoot ++ "/lib/"] - case buildOS of - OSX -> - rawSystemExit verbosity "ln" ["-s", "-f", "../wasm-chain-integration/target/release/libconcordium_smart_contract_engine.dylib", smartContractRoot ++ "/lib/libconcordium_smart_contract_engine.dylib"] - _ -> - rawSystemExit verbosity "ln" ["-s", "-f", "../wasm-chain-integration/target/release/libconcordium_smart_contract_engine.so", smartContractRoot ++ "/lib/libconcordium_smart_contract_engine.so"] + runCmd verbosity $ "ln -s -f " ++ pltWorkspace ++ "/target/release/libplt_scheduler.a " ++ libraryDestination + runCmd verbosity $ "ln -s -f " ++ pltWorkspace ++ "/target/release/libplt_scheduler.so " ++ libraryDestination return emptyHookedBuildInfo -- | On Windows, copy the DLL files to the binary install directory. This is to ensure that they -- are accessible when running the binaries, tests and benchmarks. -copyDlls :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO () -copyDlls _ flags pkgDescr lbi = case buildOS of +postCopyHook :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO () +postCopyHook _ flags pkgDescr lbi = case buildOS of Windows -> do let installDirs = absoluteComponentInstallDirs pkgDescr lbi (localUnitId lbi) copydest - let copyLib lib = do - rawSystemExit verbosity "cp" ["-u", smartContractRoot ++ "/lib/" ++ lib ++ ".dll", bindir installDirs] - notice verbosity $ "Copy " ++ lib ++ " to " ++ bindir installDirs - copyLib "concordium_smart_contract_engine" + -- Copy DLL for Concordium Smart Contract Engine + smartContractEngineCrate <- canonicalizePath smartContractEngineCrateRelative + runCmd verbosity $ "cp -u " ++ smartContractEngineCrate ++ "/target/release/concordium_smart_contract_engine.dll " ++ bindir installDirs + -- Copy DLL for PLT scheduler + pltWorkspace <- canonicalizePath pltWorkspaceRelative + runCmd verbosity $ "cp -u " ++ pltWorkspace ++ "/target/release/plt_scheduler.dll " ++ bindir installDirs _ -> return () where distPref = fromFlag (copyDistPref flags) @@ -59,6 +81,6 @@ copyDlls _ flags pkgDescr lbi = case buildOS of main = defaultMainWithHooks $ simpleUserHooks - { preConf = makeRust, - postCopy = copyDlls + { preConf = preConfHook, + postCopy = postCopyHook } diff --git a/concordium-consensus/package.yaml b/concordium-consensus/package.yaml index 160d91acb3..b41873ded1 100644 --- a/concordium-consensus/package.yaml +++ b/concordium-consensus/package.yaml @@ -88,7 +88,9 @@ library: - -O2 - -fno-ignore-asserts - extra-libraries: concordium_smart_contract_engine + extra-libraries: + - concordium_smart_contract_engine + - plt_scheduler when: - condition: "!(os(windows)) && !(flag(dynamic))" diff --git a/concordium-consensus/src/Concordium/GlobalState/BakerInfo.hs b/concordium-consensus/src/Concordium/GlobalState/BakerInfo.hs index 1f292c8f4c..40eed9e2f1 100644 --- a/concordium-consensus/src/Concordium/GlobalState/BakerInfo.hs +++ b/concordium-consensus/src/Concordium/GlobalState/BakerInfo.hs @@ -377,6 +377,7 @@ genesisBakerInfoEx spv cp GenesisBaker{..} = case spv of SP7 -> binfoV1 SP8 -> binfoV1 SP9 -> binfoV1 + SP10 -> binfoV1 where bkrInfo = BakerInfo diff --git a/concordium-consensus/src/Concordium/GlobalState/Block.hs b/concordium-consensus/src/Concordium/GlobalState/Block.hs index d25b3ca7c6..3ef3f70c47 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Block.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Block.hs @@ -144,6 +144,7 @@ blockVersion SP6 = 3 blockVersion SP7 = 3 blockVersion SP8 = 3 blockVersion SP9 = 3 +blockVersion SP10 = 3 {-# INLINE blockVersion #-} -- | Type class that supports serialization of a block. diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/Account.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/Account.hs index d8922488d4..cabac3c92d 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/Account.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/Account.hs @@ -865,6 +865,7 @@ migratePersistentAccount m@StateMigrationParametersP5ToP6{} (PAV2 acc) = PAV2 <$ migratePersistentAccount m@StateMigrationParametersP6ToP7{} (PAV2 acc) = PAV3 <$> V1.migratePersistentAccount m acc migratePersistentAccount m@StateMigrationParametersP7ToP8{} (PAV3 acc) = PAV4 <$> V1.migratePersistentAccount m acc migratePersistentAccount m@StateMigrationParametersP8ToP9{} (PAV4 acc) = PAV5 <$> V1.migratePersistentAccount m acc +migratePersistentAccount m@StateMigrationParametersP9ToP10{} (PAV5 acc) = PAV5 <$> V1.migratePersistentAccount m acc -- | Migrate a 'PersistentBakerInfoRef' between protocol versions according to a state migration. migratePersistentBakerInfoRef :: @@ -887,6 +888,7 @@ migratePersistentBakerInfoRef m@StateMigrationParametersP5ToP6{} (PBIRV2 bir) = migratePersistentBakerInfoRef m@StateMigrationParametersP6ToP7{} (PBIRV2 bir) = PBIRV3 <$> V1.migratePersistentBakerInfoEx m bir migratePersistentBakerInfoRef m@StateMigrationParametersP7ToP8{} (PBIRV3 bir) = PBIRV4 <$> V1.migratePersistentBakerInfoEx m bir migratePersistentBakerInfoRef m@StateMigrationParametersP8ToP9{} (PBIRV4 bir) = PBIRV5 <$> V1.migratePersistentBakerInfoEx m bir +migratePersistentBakerInfoRef m@StateMigrationParametersP9ToP10{} (PBIRV5 bir) = PBIRV5 <$> V1.migratePersistentBakerInfoEx m bir -- * Conversion diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/Account/StructureV1.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/Account/StructureV1.hs index ceefb9d73a..dc51bd7d2f 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/Account/StructureV1.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/Account/StructureV1.hs @@ -134,6 +134,7 @@ migratePersistentBakerInfoEx StateMigrationParametersP7ToP8{} = migrateReference m' (BakerInfoEx av2) migrateBakerInfoExV1 BakerInfoExV1{..} = return BakerInfoExV1{_bieIsSuspended = CTrue False, ..} migratePersistentBakerInfoEx StateMigrationParametersP8ToP9{} = migrateReference (return . coerceBakerInfoExV1) +migratePersistentBakerInfoEx StateMigrationParametersP9ToP10{} = migrateReference (return . coerceBakerInfoExV1) -- | Migrate a 'V0.PersistentBakerInfoEx' to a 'PersistentBakerInfoEx'. -- See documentation of @migratePersistentBlockState@. @@ -2472,6 +2473,7 @@ migratePersistentAccount StateMigrationParametersP5ToP6{} acc = migrateV2ToV2 ac migratePersistentAccount StateMigrationParametersP6ToP7{} acc = migrateV2ToV3 acc migratePersistentAccount StateMigrationParametersP7ToP8{} acc = migrateV3ToV4 acc migratePersistentAccount StateMigrationParametersP8ToP9{} acc = migrateV4ToV5 acc +migratePersistentAccount StateMigrationParametersP9ToP10{} acc = migrateV5ToV5 acc -- | Migration for 'PersistentAccount' from 'V0.PersistentAccount'. This supports migration from -- 'P4' to 'P5'. diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/Bakers.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/Bakers.hs index 8358efa7f6..94ef038e5b 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/Bakers.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/Bakers.hs @@ -159,6 +159,8 @@ migratePersistentEpochBakers migration PersistentEpochBakers{..} = do SomeParam $ unOParam _bakerFinalizationCommitteeParameters StateMigrationParametersP8ToP9{} -> SomeParam $ unOParam _bakerFinalizationCommitteeParameters + StateMigrationParametersP9ToP10{} -> + SomeParam $ unOParam _bakerFinalizationCommitteeParameters return PersistentEpochBakers { _bakerInfos = newBakerInfos, @@ -336,6 +338,10 @@ migratePersistentActiveDelegators StateMigrationParametersP8ToP9{} = \case PersistentActiveDelegatorsV1{..} -> do newDelegators <- Trie.migrateTrieN True return adDelegators return PersistentActiveDelegatorsV1{adDelegators = newDelegators, ..} +migratePersistentActiveDelegators StateMigrationParametersP9ToP10{} = \case + PersistentActiveDelegatorsV1{..} -> do + newDelegators <- Trie.migrateTrieN True return adDelegators + return PersistentActiveDelegatorsV1{adDelegators = newDelegators, ..} emptyPersistentActiveDelegators :: forall av. (IsAccountVersion av) => PersistentActiveDelegators av emptyPersistentActiveDelegators = @@ -387,6 +393,7 @@ migrateTotalActiveCapital StateMigrationParametersP5ToP6{} _ (TotalActiveCapital migrateTotalActiveCapital StateMigrationParametersP6ToP7{} _ (TotalActiveCapitalV1 bts) = TotalActiveCapitalV1 bts migrateTotalActiveCapital StateMigrationParametersP7ToP8{} _ (TotalActiveCapitalV1 bts) = TotalActiveCapitalV1 bts migrateTotalActiveCapital StateMigrationParametersP8ToP9{} _ (TotalActiveCapitalV1 bts) = TotalActiveCapitalV1 bts +migrateTotalActiveCapital StateMigrationParametersP9ToP10{} _ (TotalActiveCapitalV1 bts) = TotalActiveCapitalV1 bts instance (IsAccountVersion av) => Serialize (TotalActiveCapital av) where put TotalActiveCapitalV0 = return () diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs index 4b1d58a061..3737c7b3ad 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState.hs @@ -187,6 +187,7 @@ migrateSeedState (StateMigrationParametersP5ToP6 (P6.StateMigrationData _ time)) migrateSeedState StateMigrationParametersP6ToP7{} ss = migrateSeedStateV1Trivial ss migrateSeedState StateMigrationParametersP7ToP8{} ss = migrateSeedStateV1Trivial ss migrateSeedState StateMigrationParametersP8ToP9{} ss = migrateSeedStateV1Trivial ss +migrateSeedState StateMigrationParametersP9ToP10{} ss = migrateSeedStateV1Trivial ss -- | Trivial migration of a 'SeedStateV1' between protocol versions. migrateSeedStateV1Trivial :: SeedState 'SeedStateVersion1 -> SeedState 'SeedStateVersion1 @@ -642,6 +643,10 @@ migrateBlockRewardDetails StateMigrationParametersP8ToP9{} _ _ (SomeParam TimePa (BlockRewardDetailsV1 hbr) -> BlockRewardDetailsV1 <$> migrateHashedBufferedRef (migratePoolRewardsP6 oldEpoch _tpRewardPeriodLength) hbr +migrateBlockRewardDetails StateMigrationParametersP9ToP10{} _ _ (SomeParam TimeParametersV1{..}) oldEpoch = \case + (BlockRewardDetailsV1 hbr) -> + BlockRewardDetailsV1 + <$> migrateHashedBufferedRef (migratePoolRewardsP6 oldEpoch _tpRewardPeriodLength) hbr instance (MonadBlobStore m, IsBlockHashVersion bhv, IsAccountVersion av) => @@ -2848,6 +2853,7 @@ doGetRewardStatus pbs = do SP7 -> rewardsV1 SP8 -> rewardsV1 SP9 -> rewardsV1 + SP10 -> rewardsV1 doRewardFoundationAccount :: (SupportsPersistentState pv m) => PersistentBlockState pv -> Amount -> m (PersistentBlockState pv) doRewardFoundationAccount pbs reward = do @@ -2976,6 +2982,7 @@ doModifyAccount pbs aUpd@AccountUpdate{..} = do SP7 -> return _auIndex SP8 -> return _auIndex SP9 -> return _auIndex + SP10 -> return _auIndex !oldRel <- accountNextReleaseTimestamp acc !newRel <- accountNextReleaseTimestamp acc' return (acctRef :: RSAccountRef pv, oldRel, newRel) @@ -3679,6 +3686,7 @@ doProcessReleaseSchedule pbs ts = do SP7 -> processAccountP5 SP8 -> processAccountP5 SP9 -> processAccountP5 + SP10 -> processAccountP5 (newAccs, newRS) <- foldM processAccount (bspAccounts bsp, remRS) affectedAccounts storePBS pbs (bsp{bspAccounts = newAccs, bspReleaseSchedule = newRS}) @@ -4874,6 +4882,7 @@ migrateBlockPointers migration BlockStatePointers{..} = do StateMigrationParametersP6ToP7{} -> RSMNewToNew StateMigrationParametersP7ToP8{} -> RSMNewToNew StateMigrationParametersP8ToP9{} -> RSMNewToNew + StateMigrationParametersP9ToP10{} -> RSMNewToNew logEvent GlobalState LLTrace "Migrating release schedule" newReleaseSchedule <- migrateReleaseSchedule rsMigration bspReleaseSchedule pab <- lift . refLoad $ bspBirkParameters ^. birkActiveBakers diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs index a6bd08a0e7..bea304ccdd 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Modules.hs @@ -577,6 +577,7 @@ migrateModules migration mods = do StateMigrationParametersP6ToP7{} -> migrateToP7 @v wasmMod -- always recompile to lower transaction costs. StateMigrationParametersP7ToP8{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} StateMigrationParametersP8ToP9{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} + StateMigrationParametersP9ToP10{} -> return $! moduleVInterface{GSWasm.miModule = PIMVMem artifact} -- store the module into the new state, and remove it from memory makeFlushedHashedCachedRef $! diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Updates.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Updates.hs index a9ee18a79e..bb1b1e0f0b 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Updates.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/BlockState/Updates.hs @@ -317,6 +317,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa NoParam -> return NoParam StateMigrationParametersP8ToP9{} -> case pElectionDifficultyQueue of NoParam -> return NoParam + StateMigrationParametersP9ToP10{} -> case pElectionDifficultyQueue of + NoParam -> return NoParam newTimeParameters <- case migration of StateMigrationParametersTrivial -> case pTimeParametersQueue of NoParam -> return NoParam @@ -338,6 +340,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr StateMigrationParametersP8ToP9{} -> case pTimeParametersQueue of SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + StateMigrationParametersP9ToP10{} -> case pTimeParametersQueue of + SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr newCooldownParameters <- case migration of StateMigrationParametersTrivial -> case pCooldownParametersQueue of NoParam -> return NoParam @@ -360,6 +364,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr StateMigrationParametersP8ToP9{} -> case pCooldownParametersQueue of SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + StateMigrationParametersP9ToP10{} -> case pCooldownParametersQueue of + SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr newTimeoutParameters <- case migration of StateMigrationParametersTrivial -> case pTimeoutParametersQueue of NoParam -> return NoParam @@ -381,6 +387,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr StateMigrationParametersP8ToP9{} -> case pTimeoutParametersQueue of SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + StateMigrationParametersP9ToP10{} -> case pTimeoutParametersQueue of + SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr newMinBlockTimeQueue <- case migration of StateMigrationParametersTrivial -> case pMinBlockTimeQueue of NoParam -> return NoParam @@ -402,6 +410,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr StateMigrationParametersP8ToP9{} -> case pMinBlockTimeQueue of SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + StateMigrationParametersP9ToP10{} -> case pMinBlockTimeQueue of + SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr newBlockEnergyLimitQueue <- case migration of StateMigrationParametersTrivial -> case pBlockEnergyLimitQueue of NoParam -> return NoParam @@ -423,6 +433,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr StateMigrationParametersP8ToP9{} -> case pBlockEnergyLimitQueue of SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + StateMigrationParametersP9ToP10{} -> case pBlockEnergyLimitQueue of + SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr newFinalizationCommitteeParametersQueue <- case migration of StateMigrationParametersTrivial -> case pFinalizationCommitteeParametersQueue of NoParam -> return NoParam @@ -444,6 +456,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr StateMigrationParametersP8ToP9{} -> case pFinalizationCommitteeParametersQueue of SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + StateMigrationParametersP9ToP10{} -> case pFinalizationCommitteeParametersQueue of + SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr newValidatorScoreParametersQueue <- case migration of StateMigrationParametersTrivial -> case pValidatorScoreParametersQueue of NoParam -> return NoParam @@ -465,6 +479,8 @@ migratePendingUpdates migration PendingUpdates{..} = withCPVConstraints (chainPa return (SomeParam hbr) StateMigrationParametersP8ToP9{} -> case pValidatorScoreParametersQueue of SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr + StateMigrationParametersP9ToP10{} -> case pValidatorScoreParametersQueue of + SomeParam hbr -> SomeParam <$> migrateHashedBufferedRef (migrateUpdateQueue id) hbr return $! PendingUpdates { pRootKeysUpdateQueue = newRootKeys, @@ -812,6 +828,7 @@ migrateUpdates migration Updates{..} = do StateMigrationParametersP6ToP7 -> CFalse StateMigrationParametersP7ToP8 _ -> CFalse StateMigrationParametersP8ToP9 _ -> CTrue minUpdateSequenceNumber + StateMigrationParametersP9ToP10 _ -> CTrue minUpdateSequenceNumber return Updates { currentKeyCollection = newKeyCollection, diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/Genesis.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/Genesis.hs index bbd0107f41..4987c2d501 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/Genesis.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/Genesis.hs @@ -12,6 +12,7 @@ module Concordium.GlobalState.Persistent.Genesis (genesisState) where import qualified Concordium.Genesis.Data as GenesisData import qualified Concordium.Genesis.Data.BaseV1 as GDBaseV1 import qualified Concordium.Genesis.Data.P1 as P1 +import qualified Concordium.Genesis.Data.P10 as P10 import qualified Concordium.Genesis.Data.P2 as P2 import qualified Concordium.Genesis.Data.P3 as P3 import qualified Concordium.Genesis.Data.P4 as P4 @@ -90,6 +91,9 @@ genesisState gd = MTL.runExceptT $ case Types.protocolVersion @pv of Types.SP9 -> case gd of GenesisData.GDP9 P9.GDP9Initial{..} -> buildGenesisBlockState (CGPV1 genesisCore) genesisInitialState + Types.SP10 -> case gd of + GenesisData.GDP10 P10.GDP10Initial{..} -> + buildGenesisBlockState (CGPV1 genesisCore) genesisInitialState -------- Types ----------- diff --git a/concordium-consensus/src/Concordium/GlobalState/Persistent/ReleaseSchedule.hs b/concordium-consensus/src/Concordium/GlobalState/Persistent/ReleaseSchedule.hs index 1dd66af4c1..5f9f8bbe3d 100644 --- a/concordium-consensus/src/Concordium/GlobalState/Persistent/ReleaseSchedule.hs +++ b/concordium-consensus/src/Concordium/GlobalState/Persistent/ReleaseSchedule.hs @@ -310,6 +310,7 @@ instance (MonadBlobStore m, IsProtocolVersion pv) => BlobStorable m (ReleaseSche SP7 -> fmap ReleaseScheduleP5 <$> load SP8 -> fmap ReleaseScheduleP5 <$> load SP9 -> fmap ReleaseScheduleP5 <$> load + SP10 -> fmap ReleaseScheduleP5 <$> load instance (MonadBlobStore m) => Cacheable m (ReleaseSchedule pv) where cache (ReleaseScheduleP0 rs) = ReleaseScheduleP0 <$> cache rs @@ -342,6 +343,7 @@ emptyReleaseSchedule = case protocolVersion @pv of SP7 -> rsP1 SP8 -> rsP1 SP9 -> rsP1 + SP10 -> rsP1 where rsP0 :: (RSAccountRef pv ~ AccountAddress) => m (ReleaseSchedule pv) rsP0 = do @@ -391,6 +393,7 @@ trivialReleaseScheduleMigration = case protocolVersion @pv of SP7 -> RSMNewToNew SP8 -> RSMNewToNew SP9 -> RSMNewToNew + SP10 -> RSMNewToNew -- | Migrate a release schedule from one protocol version to another, given by a -- 'ReleaseScheduleMigration'. diff --git a/concordium-consensus/src/Concordium/KonsensusV1/TestMonad.hs b/concordium-consensus/src/Concordium/KonsensusV1/TestMonad.hs index 51575d6adb..1ea905ac51 100644 --- a/concordium-consensus/src/Concordium/KonsensusV1/TestMonad.hs +++ b/concordium-consensus/src/Concordium/KonsensusV1/TestMonad.hs @@ -25,6 +25,7 @@ import Concordium.TimeMonad import Concordium.Types import qualified Concordium.Genesis.Data.BaseV1 as BaseV1 +import qualified Concordium.Genesis.Data.P10 as P10 import qualified Concordium.Genesis.Data.P6 as P6 import qualified Concordium.Genesis.Data.P7 as P7 import qualified Concordium.Genesis.Data.P8 as P8 @@ -33,7 +34,7 @@ import qualified Concordium.GlobalState.AccountMap.LMDB as LMDBAccountMap import Concordium.GlobalState.BlockState import qualified Concordium.GlobalState.ContractStateV1 as StateV1 import Concordium.GlobalState.Parameters ( - GenesisData (GDP6, GDP7, GDP8, GDP9), + GenesisData (GDP10, GDP6, GDP7, GDP8, GDP9), defaultRuntimeParameters, genesisBlockHash, ) @@ -153,6 +154,7 @@ genesisCore = case protocolVersion @pv of SP7 -> \(GDP7 P7.GDP7Initial{genesisCore = core}) -> core SP8 -> \(GDP8 P8.GDP8Initial{genesisCore = core}) -> core SP9 -> \(GDP9 P9.GDP9Initial{genesisCore = core}) -> core + SP10 -> \(GDP10 P10.GDP10Initial{genesisCore = core}) -> core -- | Run an operation in the 'TestMonad' with the given baker, time and genesis data. -- This sets up a temporary blob store for the block state that is deleted after use. diff --git a/concordium-consensus/src/Concordium/PLTScheduler.hs b/concordium-consensus/src/Concordium/PLTScheduler.hs new file mode 100644 index 0000000000..d94113711c --- /dev/null +++ b/concordium-consensus/src/Concordium/PLTScheduler.hs @@ -0,0 +1,41 @@ +{-# LANGUAGE OverloadedStrings #-} + +-- | +-- Bindings into the @plt-scheduler@ Rust library exposing safe wrappers. +-- +-- Each foreign imported function must match the signature of functions found in @plt-scheduler/src/ffi.rs@. +module Concordium.PLTScheduler ( + executeTransaction, +) where + +import qualified Data.ByteString as BS +import qualified Data.ByteString.Unsafe as BS +import qualified Data.Word as Word +import qualified Foreign as FFI +import qualified Foreign.C.Types as FFI + +-- | Execute a transaction payload modifying the `block_state` accordingly. +-- The caller must ensure to rollback state changes in case of the transaction being rejected. +-- +-- See @execute_transaction@ in @plt-scheduler@ rust crate for details. +executeTransaction :: + -- | Transaction payload byte string. + BS.ByteString -> + -- | The events produced or the reject reason. + IO (Either () ()) +executeTransaction transactionPayload = do + statusCode <- BS.unsafeUseAsCStringLen transactionPayload $ \(transactionPayloadPtr, transactionPayloadLen) -> do + ffiExecuteTransaction (FFI.castPtr transactionPayloadPtr) (fromIntegral transactionPayloadLen) + case statusCode of + 0 -> return $ Right () + 1 -> return $ Left () + _ -> error "Unexpected status code from calling 'ffiExecuteTransaction'" + +foreign import ccall "ffi_execute_transaction" + ffiExecuteTransaction :: + -- | Pointer to transaction payload bytes. + FFI.Ptr Word.Word8 -> + -- | Byte length of transaction payload. + FFI.CSize -> + -- | Status code + IO Word.Word8 diff --git a/concordium-consensus/src/Concordium/ProtocolUpdate/P10.hs b/concordium-consensus/src/Concordium/ProtocolUpdate/P10.hs new file mode 100644 index 0000000000..cd3f70ef68 --- /dev/null +++ b/concordium-consensus/src/Concordium/ProtocolUpdate/P10.hs @@ -0,0 +1,61 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} + +module Concordium.ProtocolUpdate.P10 ( + Update (..), + checkUpdate, + updateRegenesis, + updateNextProtocolVersion, +) where + +import Control.Monad.State +import qualified Data.HashMap.Strict as HM +import Data.Serialize + +import qualified Concordium.Crypto.SHA256 as SHA256 +import Concordium.Types +import Concordium.Types.Updates + +import Concordium.GlobalState.BlockState +import qualified Concordium.GlobalState.Persistent.BlockState as PBS +import Concordium.GlobalState.Types +import qualified Concordium.GlobalState.Types as GSTypes +import Concordium.KonsensusV1.TreeState.Implementation +import Concordium.KonsensusV1.TreeState.Types +import qualified Concordium.ProtocolUpdate.P10.Reboot as Reboot + +-- | Updates that are supported from protocol version P10. +data Update = Reboot + deriving (Show) + +-- | Hash map for resolving updates from their specification hash. +updates :: HM.HashMap SHA256.Hash (Get Update) +updates = HM.fromList [(Reboot.updateHash, return Reboot)] + +-- | Determine if a 'ProtocolUpdate' corresponds to a supported update type. +checkUpdate :: ProtocolUpdate -> Either String Update +checkUpdate ProtocolUpdate{..} = case HM.lookup puSpecificationHash updates of + Nothing -> Left "Specification hash does not correspond to a known protocol update." + Just updateGet -> case runGet updateGet puSpecificationAuxiliaryData of + Left err -> Left $! "Could not deserialize auxiliary data: " ++ err + Right update -> return update + +-- | Construct the genesis data for a P10 update. +updateRegenesis :: + ( MPV m ~ 'P10, + BlockStateStorage m, + MonadState (SkovData (MPV m)) m, + GSTypes.BlockState m ~ PBS.HashedPersistentBlockState (MPV m) + ) => + -- | The update taking effect. + Update -> + -- | The terminal block of the old chain. + BlockPointer (MPV m) -> + m (PVInit m) +updateRegenesis Reboot = Reboot.updateRegenesis + +-- | Determine the protocol version the update will update to. +updateNextProtocolVersion :: + Update -> + SomeProtocolVersion +updateNextProtocolVersion Reboot{} = SomeProtocolVersion SP10 diff --git a/concordium-consensus/src/Concordium/ProtocolUpdate/P10/Reboot.hs b/concordium-consensus/src/Concordium/ProtocolUpdate/P10/Reboot.hs new file mode 100644 index 0000000000..63a0140d3b --- /dev/null +++ b/concordium-consensus/src/Concordium/ProtocolUpdate/P10/Reboot.hs @@ -0,0 +1,111 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeFamilies #-} + +-- | This module implements the P10.Reboot protocol update. +-- This protocol update is valid at protocol version P10, and updates +-- to protocol version P10. +-- This produces a new 'RegenesisDataP10 using the 'GDP10Regenesis' constructor, +-- as follows: +-- +-- * 'genesisCore': +-- +-- * 'genesisTime' is the timestamp of the last finalized block of the previous chain. +-- * 'genesisEpochDuration' is taken from the previous genesis. +-- * 'genesisSignatureThreshold' is taken from the previous genesis. +-- +-- * 'genesisFirstGenesis' is either: +-- +-- * the hash of the genesis block of the previous chain, if it is a 'GDP10Initial'; or +-- * the 'genesisFirstGenesis' value of the genesis block of the previous chain, if it +-- is a 'GDP10Regenesis'. +-- +-- * 'genesisPreviousGenesis' is the hash of the previous genesis block. +-- +-- * 'genesisTerminalBlock' is the hash of the last finalized block of the previous chain. +-- +-- * 'genesisStateHash' is the state hash of the last finalized block of the previous chain. +-- +-- The block state is taken from the last finalized block of the previous chain. It is updated +-- as part of the state migration, which makes the following changes: +-- +-- * The seed state is migrated as follows: +-- +-- * The current epoch is reset to zero. +-- * The current and updated leadership election nonce are set to the hash of +-- @"Regenesis" <> encode oldUpdatedNonce@. +-- * The trigger block time is kept the same, meaning that the epoch will transition as soon +-- as possible. +-- * The epoch transition triggered flag is set. +-- * The shutdown triggered flag is cleared. +-- +-- * The old current epoch is subtracted from the next payday epoch. +-- +-- * The protocol update queue is emptied during the migration. +-- +-- Note that, the initial epoch of the new chain is not considered +-- a new epoch for the purposes of block rewards and baker/finalization committee determination. +-- In particular, the timing of the next payday will be the same as if the protocol update +-- had not happened. (For instance, if it would have happened at the start of the next epoch +-- prior to the protocol update, after the update it will happen at the start of epoch 1. +-- The trigger block time in epoch 0 of the new consensus is the same as the trigger block +-- time in the final epoch of the old consensus.) +-- Furthermore, the bakers from the final epoch of the previous chain are also the bakers for the +-- initial epoch of the new chain. +module Concordium.ProtocolUpdate.P10.Reboot where + +import Control.Monad.State +import Lens.Micro.Platform + +import qualified Concordium.Crypto.SHA256 as SHA256 +import qualified Concordium.Genesis.Data as GenesisData +import qualified Concordium.Genesis.Data.BaseV1 as BaseV1 +import qualified Concordium.Genesis.Data.P10 as P10 +import Concordium.GlobalState.BlockState +import qualified Concordium.GlobalState.Persistent.BlockState as PBS +import Concordium.GlobalState.Types +import qualified Concordium.GlobalState.Types as GSTypes +import Concordium.KonsensusV1.TreeState.Implementation +import Concordium.KonsensusV1.TreeState.Types +import Concordium.KonsensusV1.Types +import Concordium.Types.HashableTo (getHash) +import Concordium.Types.ProtocolVersion + +-- | The hash that identifies the P10.Reboot update: +-- e135d02624bcf91d8184c6746f6b2fc2e869df0b2716693e47e5ece8ec4d9704 +updateHash :: SHA256.Hash +updateHash = SHA256.hash "P10.Reboot" + +-- | Construct the genesis data for a P10.Reboot update. +-- This takes the terminal block of the old chain which is used as the basis for constructing +-- the new genesis block. +updateRegenesis :: + ( MPV m ~ 'P10, + BlockStateStorage m, + MonadState (SkovData (MPV m)) m, + GSTypes.BlockState m ~ PBS.HashedPersistentBlockState (MPV m) + ) => + -- | The terminal block of the old chain. + BlockPointer 'P10 -> + m (PVInit m) +updateRegenesis terminal = do + -- Genesis time is the timestamp of the terminal block + let regenesisTime = blockTimestamp terminal + -- Core parameters are derived from the old genesis, apart from genesis time which is set for + -- the time of the terminal block. + gMetadata <- use genesisMetadata + BaseV1.CoreGenesisParametersV1{..} <- gmParameters <$> use genesisMetadata + let core = + BaseV1.CoreGenesisParametersV1 + { BaseV1.genesisTime = regenesisTime, + .. + } + -- genesisFirstGenesis is the block hash of the previous genesis, if it is initial, + -- or the genesisFirstGenesis of the previous genesis otherwise. + let genesisFirstGenesis = gmFirstGenesisHash gMetadata + genesisPreviousGenesis = gmCurrentGenesisHash gMetadata + genesisTerminalBlock = getHash terminal + let regenesisBlockState = bpState terminal + genesisStateHash <- getStateHash regenesisBlockState + let newGenesis = GenesisData.RGDP10 $ P10.GDP10Regenesis{genesisRegenesis = BaseV1.RegenesisDataV1{genesisCore = core, ..}} + return (PVInit newGenesis GenesisData.StateMigrationParametersTrivial (bmHeight $ bpInfo terminal)) diff --git a/concordium-consensus/src/Concordium/ProtocolUpdate/P9/Reboot.hs b/concordium-consensus/src/Concordium/ProtocolUpdate/P9/Reboot.hs index 3f5f34e610..baad9187ca 100644 --- a/concordium-consensus/src/Concordium/ProtocolUpdate/P9/Reboot.hs +++ b/concordium-consensus/src/Concordium/ProtocolUpdate/P9/Reboot.hs @@ -76,7 +76,7 @@ import Concordium.Types.ProtocolVersion updateHash :: SHA256.Hash updateHash = SHA256.hash "P9.Reboot" --- | Construct the genesis data for a P8.Reboot update. +-- | Construct the genesis data for a P9.Reboot update. -- This takes the terminal block of the old chain which is used as the basis for constructing -- the new genesis block. updateRegenesis :: diff --git a/concordium-consensus/src/Concordium/ProtocolUpdate/V1.hs b/concordium-consensus/src/Concordium/ProtocolUpdate/V1.hs index 3f04fcc4d8..5a1c070156 100644 --- a/concordium-consensus/src/Concordium/ProtocolUpdate/V1.hs +++ b/concordium-consensus/src/Concordium/ProtocolUpdate/V1.hs @@ -17,6 +17,7 @@ import Concordium.GlobalState.Types (PVInit) import qualified Concordium.GlobalState.Types as GSTypes import Concordium.KonsensusV1.TreeState.Implementation import Concordium.KonsensusV1.TreeState.Types +import qualified Concordium.ProtocolUpdate.P10 as P10 import qualified Concordium.ProtocolUpdate.P6 as P6 import qualified Concordium.ProtocolUpdate.P7 as P7 import qualified Concordium.ProtocolUpdate.P8 as P8 @@ -28,12 +29,14 @@ data Update (pv :: ProtocolVersion) where UpdateP7 :: P7.Update -> Update 'P7 UpdateP8 :: P8.Update -> Update 'P8 UpdateP9 :: P9.Update -> Update 'P9 + UpdateP10 :: P10.Update -> Update 'P10 instance Show (Update pv) where show (UpdateP6 u) = "P6." ++ show u show (UpdateP7 u) = "P7." ++ show u show (UpdateP8 u) = "P8." ++ show u - show (UpdateP9 u) = "P8." ++ show u + show (UpdateP9 u) = "P9." ++ show u + show (UpdateP10 u) = "P10." ++ show u -- | Determine if a 'ProtocolUpdate' corresponds to a supported update type. checkUpdate :: forall pv. (IsProtocolVersion pv) => ProtocolUpdate -> Either String (Update pv) @@ -49,6 +52,7 @@ checkUpdate = case protocolVersion @pv of SP7 -> fmap UpdateP7 . P7.checkUpdate SP8 -> fmap UpdateP8 . P8.checkUpdate SP9 -> fmap UpdateP9 . P9.checkUpdate + SP10 -> fmap UpdateP10 . P10.checkUpdate -- | Construct the genesis data for a P1 update. updateRegenesis :: @@ -65,6 +69,7 @@ updateRegenesis (UpdateP6 u) = P6.updateRegenesis u updateRegenesis (UpdateP7 u) = P7.updateRegenesis u updateRegenesis (UpdateP8 u) = P8.updateRegenesis u updateRegenesis (UpdateP9 u) = P9.updateRegenesis u +updateRegenesis (UpdateP10 u) = P10.updateRegenesis u -- | Determine the next protocol version for the given update. Although the same -- information can be retrieved from 'updateRegenesis', this is more efficient @@ -76,3 +81,4 @@ updateNextProtocolVersion (UpdateP6 u) = P6.updateNextProtocolVersion u updateNextProtocolVersion (UpdateP7 u) = P7.updateNextProtocolVersion u updateNextProtocolVersion (UpdateP8 u) = P8.updateNextProtocolVersion u updateNextProtocolVersion (UpdateP9 u) = P9.updateNextProtocolVersion u +updateNextProtocolVersion (UpdateP10 u) = P10.updateNextProtocolVersion u diff --git a/concordium-consensus/src/Concordium/Scheduler.hs b/concordium-consensus/src/Concordium/Scheduler.hs index d32271d339..f83c324ca8 100644 --- a/concordium-consensus/src/Concordium/Scheduler.hs +++ b/concordium-consensus/src/Concordium/Scheduler.hs @@ -1283,6 +1283,7 @@ handleContractUpdateV1 depth originAddr istance checkAndGetSender transferAmount P7 -> resumeEvent False : interruptEvent : events P8 -> resumeEvent False : interruptEvent : events P9 -> resumeEvent False : interruptEvent : events + P10 -> resumeEvent False : interruptEvent : events go newEvents =<< runInterpreter (return . WasmV1.resumeReceiveFun rrdInterruptedConfig rrdCurrentState False entryBalance (WasmV1.Error (WasmV1.EnvFailure (WasmV1.MissingContract imcTo))) Nothing) Just (InstanceInfoV0 targetInstance) -> do -- we are invoking a V0 instance. diff --git a/concordium-consensus/src/Concordium/Scheduler/TreeStateEnvironment.hs b/concordium-consensus/src/Concordium/Scheduler/TreeStateEnvironment.hs index 69f73f8389..b6888edd23 100644 --- a/concordium-consensus/src/Concordium/Scheduler/TreeStateEnvironment.hs +++ b/concordium-consensus/src/Concordium/Scheduler/TreeStateEnvironment.hs @@ -1183,6 +1183,7 @@ putBakerCommissionsInRange ranges bs (BakerId ai) = case protocolVersion @(MPV m SP7 -> bsoConstrainBakerCommission bs ai ranges SP8 -> bsoConstrainBakerCommission bs ai ranges SP9 -> bsoConstrainBakerCommission bs ai ranges + SP10 -> bsoConstrainBakerCommission bs ai ranges -- | The result of executing the block prologue. data PrologueResult m = PrologueResult diff --git a/concordium-consensus/src/Concordium/Startup.hs b/concordium-consensus/src/Concordium/Startup.hs index b6dcfa8c88..e914e809ba 100644 --- a/concordium-consensus/src/Concordium/Startup.hs +++ b/concordium-consensus/src/Concordium/Startup.hs @@ -32,6 +32,7 @@ import Concordium.Crypto.DummyData ( import qualified Concordium.Genesis.Data as GenesisData import qualified Concordium.Genesis.Data.BaseV1 as GDBaseV1 import qualified Concordium.Genesis.Data.P1 as P1 +import qualified Concordium.Genesis.Data.P10 as P10 import qualified Concordium.Genesis.Data.P2 as P2 import qualified Concordium.Genesis.Data.P3 as P3 import qualified Concordium.Genesis.Data.P4 as P4 @@ -278,3 +279,9 @@ makeGenesisDataV1 { genesisCore = GDBaseV1.CoreGenesisParametersV1{..}, genesisInitialState = GenesisData.GenesisState{genesisAccounts = Vec.fromList genesisAccounts, ..} } + SP10 -> + GDP10 + P10.GDP10Initial + { genesisCore = GDBaseV1.CoreGenesisParametersV1{..}, + genesisInitialState = GenesisData.GenesisState{genesisAccounts = Vec.fromList genesisAccounts, ..} + } diff --git a/concordium-consensus/stack.static.yaml b/concordium-consensus/stack.static.yaml index d9bb8acafe..a3f40505b1 100644 --- a/concordium-consensus/stack.static.yaml +++ b/concordium-consensus/stack.static.yaml @@ -9,7 +9,7 @@ extra-deps: extra-lib-dirs: - ../concordium-base/lib -- ../concordium-base/smart-contracts/lib +- ./lib ghc-options: # `simpl-tick-factor` parameter here is necessary due to a bug in the ghc: https://gitlab.haskell.org/ghc/ghc/-/issues/14637#note_413425 diff --git a/concordium-consensus/stack.yaml b/concordium-consensus/stack.yaml index e2f082d3ce..9d112e9a09 100644 --- a/concordium-consensus/stack.yaml +++ b/concordium-consensus/stack.yaml @@ -44,8 +44,8 @@ extra-deps: - ../concordium-base extra-lib-dirs: -- ../concordium-base/lib -- ../concordium-base/smart-contracts/lib + - ../concordium-base/lib + - ./lib # Override default flag values for local packages and extra-deps # flags: {} diff --git a/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/Consensus/Blocks.hs b/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/Consensus/Blocks.hs index c9dc238f67..dd581dd92d 100644 --- a/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/Consensus/Blocks.hs +++ b/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/Consensus/Blocks.hs @@ -38,6 +38,7 @@ import qualified Concordium.Types.TransactionOutcomes as TransactionOutcomes import Concordium.Types.Transactions import qualified Concordium.Types.Transactions as Transactions +import qualified Concordium.Genesis.Data.P10 as P10 import qualified Concordium.Genesis.Data.P6 as P6 import qualified Concordium.Genesis.Data.P7 as P7 import qualified Concordium.Genesis.Data.P8 as P8 @@ -128,6 +129,7 @@ genesisLEN sProtocolVersion = case sProtocolVersion of SP7 -> genesisLeadershipElectionNonce $ P7.genesisInitialState $ unGDP7 $ genesisData sProtocolVersion SP8 -> genesisLeadershipElectionNonce $ P8.genesisInitialState $ unGDP8 $ genesisData sProtocolVersion SP9 -> genesisLeadershipElectionNonce $ P9.genesisInitialState $ unGDP9 $ genesisData sProtocolVersion + SP10 -> genesisLeadershipElectionNonce $ P10.genesisInitialState $ unGDP10 $ genesisData sProtocolVersion -- | Full bakers at genesis genesisFullBakers :: forall pv. (IsConsensusV1 pv, IsProtocolVersion pv) => SProtocolVersion pv -> FullBakers diff --git a/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/TransactionProcessingTest.hs b/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/TransactionProcessingTest.hs index 0baa4a31fe..bc887718b7 100644 --- a/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/TransactionProcessingTest.hs +++ b/concordium-consensus/tests/consensus/ConcordiumTests/KonsensusV1/TransactionProcessingTest.hs @@ -46,6 +46,7 @@ import qualified Concordium.Crypto.VRF as VRF import Concordium.Genesis.Data hiding (GenesisConfiguration) import qualified Concordium.Genesis.Data.Base as Base import Concordium.Genesis.Data.BaseV1 +import Concordium.Genesis.Data.P10 import Concordium.Genesis.Data.P6 import Concordium.Genesis.Data.P7 import Concordium.Genesis.Data.P8 @@ -278,6 +279,12 @@ makeTestingGenesisData idps = { genesisCore = coreGenesisParams, genesisInitialState = Base.GenesisState{..} } + SP10 -> + GDP10 + GDP10Initial + { genesisCore = coreGenesisParams, + genesisInitialState = Base.GenesisState{..} + } -- | Utility function for parrsing identity providers. readIps :: BSL.ByteString -> Maybe IdentityProviders diff --git a/concordium-node/Cargo.lock b/concordium-node/Cargo.lock index 7a9d4002ab..8537e6e368 100644 --- a/concordium-node/Cargo.lock +++ b/concordium-node/Cargo.lock @@ -757,7 +757,7 @@ dependencies = [ [[package]] name = "concordium_base" -version = "8.0.0" +version = "8.0.0-alpha.3" dependencies = [ "aes", "anyhow", @@ -805,7 +805,7 @@ dependencies = [ [[package]] name = "concordium_base_derive" -version = "1.1.0" +version = "1.1.0-alpha.3" dependencies = [ "convert_case 0.8.0", "darling", diff --git a/concordium-node/src/bin/bootstrap_checker.rs b/concordium-node/src/bin/bootstrap_checker.rs index 3bbfc4e4bb..0b7bfcb354 100644 --- a/concordium-node/src/bin/bootstrap_checker.rs +++ b/concordium-node/src/bin/bootstrap_checker.rs @@ -37,7 +37,7 @@ fn main() -> anyhow::Result<()> { let regenesis_arc: Arc = Arc::new(Regenesis::from_blocks(regenesis_blocks)); ensure!( - regenesis_arc.blocks.read().unwrap().len() > 0, + !regenesis_arc.blocks.read().unwrap().is_empty(), "Bootstrapper can't run without specifying genesis hashes." ); diff --git a/concordium-node/src/bin/bootstrapper.rs b/concordium-node/src/bin/bootstrapper.rs index abea67e83e..1f5e3f8cfa 100644 --- a/concordium-node/src/bin/bootstrapper.rs +++ b/concordium-node/src/bin/bootstrapper.rs @@ -40,7 +40,7 @@ async fn main() -> anyhow::Result<()> { let regenesis_arc: Arc = Arc::new(Regenesis::from_blocks(regenesis_blocks)); ensure!( - regenesis_arc.blocks.read().unwrap().len() > 0, + !regenesis_arc.blocks.read().unwrap().is_empty(), "Bootstrapper can't run without specifying genesis hashes." ); diff --git a/concordium-node/src/consensus_ffi/ffi.rs b/concordium-node/src/consensus_ffi/ffi.rs index 6c512b5dd0..b2a8e3b885 100644 --- a/concordium-node/src/consensus_ffi/ffi.rs +++ b/concordium-node/src/consensus_ffi/ffi.rs @@ -2383,7 +2383,7 @@ impl ConsensusContainer { use crate::grpc2::Require; let bhi = crate::grpc2::types::block_hash_input_to_ffi(block_hash).require()?; let (block_id_type, block_hash) = bhi.to_ptr(); - let token_id_len = token_id.value.as_bytes().len(); + let token_id_len = token_id.value.len(); if token_id_len > 255 { return Err(tonic::Status::invalid_argument( "TokenId: length must be at most 255 bytes", diff --git a/concordium-node/src/health.rs b/concordium-node/src/health.rs index 795e7646d9..abcada05fa 100644 --- a/concordium-node/src/health.rs +++ b/concordium-node/src/health.rs @@ -51,7 +51,7 @@ enum ServiceError<'a> { ServiceNotFound { service: &'a str }, } -impl<'a> ServiceError<'a> { +impl ServiceError<'_> { pub fn is_not_found(&self) -> bool { matches!(self, Self::ServiceNotFound { .. }) } diff --git a/plt/Cargo.lock b/plt/Cargo.lock new file mode 100644 index 0000000000..9da4daa040 --- /dev/null +++ b/plt/Cargo.lock @@ -0,0 +1,1822 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "sha2", + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "concordium-contracts-common" +version = "9.2.0" +dependencies = [ + "base64 0.21.7", + "bs58", + "chrono", + "concordium-contracts-common-derive", + "fnv", + "hashbrown 0.11.2", + "hex", + "num-bigint", + "num-integer", + "num-traits", + "rust_decimal", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "concordium-contracts-common-derive" +version = "4.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "concordium_base" +version = "8.0.0-alpha.3" +dependencies = [ + "anyhow", + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", + "bs58", + "byteorder", + "chrono", + "ciborium-io", + "ciborium-ll", + "concordium-contracts-common", + "concordium_base_derive", + "curve25519-dalek", + "derive_more 0.99.20", + "ed25519-dalek", + "either", + "ff", + "hex", + "itertools 0.14.0", + "leb128", + "libc", + "nom", + "num", + "num-bigint", + "num-traits", + "rand", + "rayon", + "rust_decimal", + "serde", + "serde_json", + "serde_with", + "sha2", + "sha3", + "subtle", + "thiserror 2.0.17", + "zeroize", +] + +[[package]] +name = "concordium_base_derive" +version = "1.1.0-alpha.3" +dependencies = [ + "convert_case 0.8.0", + "darling 0.20.11", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "group", + "rand_core", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.111", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.111", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.111", +] + +[[package]] +name = "derive_more" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.111", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.12", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "plt-scheduler" +version = "0.1.0" +dependencies = [ + "concordium_base", + "derive_more 2.1.0", + "libc", + "plt-token-module", +] + +[[package]] +name = "plt-token-module" +version = "0.1.0" +dependencies = [ + "assert_matches", + "concordium_base", + "thiserror 2.0.17", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_with" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.12.1", + "schemars 0.9.0", + "schemars 1.1.0", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "rand_core", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap 2.12.1", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "uuid" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.111", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] diff --git a/plt/Cargo.toml b/plt/Cargo.toml new file mode 100644 index 0000000000..beed57c15d --- /dev/null +++ b/plt/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +resolver = "3" + +members = [ + "plt-scheduler", + "plt-token-module" +] + +[workspace.dependencies] +plt-token-module = {path = "plt-token-module"} + +concordium_base = {path = "../concordium-base/rust-src/concordium_base"} + +assert_matches = "1.5.0" +libc = "0.2.178" +thiserror = "2.0.17" diff --git a/plt/plt-scheduler/Cargo.toml b/plt/plt-scheduler/Cargo.toml new file mode 100644 index 0000000000..d792c8782f --- /dev/null +++ b/plt/plt-scheduler/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "plt-scheduler" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib", "staticlib", "rlib"] + +[features] +ffi = ["dep:libc"] + +[dependencies] +concordium_base.workspace = true +plt-token-module.workspace = true +derive_more = { version = "2.1.0", features = ["into", "from"] } +libc = { workspace = true, optional = true } diff --git a/plt/plt-scheduler/README.md b/plt/plt-scheduler/README.md new file mode 100644 index 0000000000..c1c236b9b5 --- /dev/null +++ b/plt/plt-scheduler/README.md @@ -0,0 +1,8 @@ +# concordium-node: Scheduler implementation for Protocol-level token (PLT) + +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](https://github.com/Concordium/.github/blob/main/.github/CODE_OF_CONDUCT.md) +![Build and test](https://github.com/Concordium/concordium-node/actions/workflows/plt-scheduler-build-test.yaml/badge.svg) + +This crate provides a scheduler (transaction execution) implementation for concordium-node, and currently only supports transactions related to Protocol-level Tokens. + +It is compiled as a native library and is used within the Haskell implemented scheduler found as part of [`concordium-consensus`](../concordium-consensus/README.md). diff --git a/plt/plt-scheduler/src/block_state.rs b/plt/plt-scheduler/src/block_state.rs new file mode 100644 index 0000000000..bf74871613 --- /dev/null +++ b/plt/plt-scheduler/src/block_state.rs @@ -0,0 +1,76 @@ +use crate::BlockStateOperations; + +pub struct BlockState {} + +impl BlockStateOperations for BlockState { + fn get_plt_list( + &self, + ) -> impl std::iter::Iterator { + // TODO implement this. The implementation below is just to help the type checker infer + // enough for this to compile + Vec::new().into_iter() + } + + fn get_token_index( + &self, + _token_id: concordium_base::protocol_level_tokens::TokenId, + ) -> Option { + todo!() + } + + fn get_mutable_token_state(&self, _token_index: crate::TokenIndex) -> crate::MutableTokenState { + todo!() + } + + fn get_token_configuration(&self, _token_index: crate::TokenIndex) -> crate::PLTConfiguration { + todo!() + } + + fn get_token_circulating_supply( + &self, + _token_index: crate::TokenIndex, + ) -> plt_token_module::token_kernel_interface::RawTokenAmount { + todo!() + } + + fn set_token_circulating_supply( + &mut self, + _token_index: crate::TokenIndex, + _circulating_supply: plt_token_module::token_kernel_interface::RawTokenAmount, + ) { + todo!() + } + + fn create_token(&mut self, _configuration: crate::PLTConfiguration) -> crate::TokenIndex { + todo!() + } + + fn update_token_account_balance( + &mut self, + _token_index: crate::TokenIndex, + _account_index: concordium_base::base::AccountIndex, + _amount_delta: crate::TokenAmountDelta, + ) -> Result<(), crate::OverflowError> { + todo!() + } + + fn touch_token_account( + &mut self, + _token_index: crate::TokenIndex, + _account_index: concordium_base::base::AccountIndex, + ) -> bool { + todo!() + } + + fn increment_plt_update_sequence_number(&mut self) { + todo!() + } + + fn set_token_state( + &mut self, + _token_index: crate::TokenIndex, + _mutable_token_state: crate::MutableTokenState, + ) { + todo!() + } +} diff --git a/plt/plt-scheduler/src/ffi.rs b/plt/plt-scheduler/src/ffi.rs new file mode 100644 index 0000000000..9b30c13d2f --- /dev/null +++ b/plt/plt-scheduler/src/ffi.rs @@ -0,0 +1,54 @@ +//! This module provides a C ABI for this library. +//! It is only available if the `ffi` feature is enabled. + +use libc::size_t; + +/// C-binding for calling [`crate::execute_transaction`]. +/// +/// Returns a byte representing the status code, where the value should be interpreted as: +/// +/// - `0` execution succeeded. +/// - `1` rejected due to ... +/// +/// # Arguments +/// +/// - `payload` Pointer to transaction payload bytes. +/// - `payload_len` Byte length of transaction payload. +/// +/// # Safety +/// +/// - Argument `payload` must be non-null and valid for reads for `payload_len` many bytes. +#[unsafe(no_mangle)] +unsafe extern "C" fn ffi_execute_transaction(payload: *const u8, payload_len: size_t) -> u8 { + debug_assert!(!payload.is_null(), "Payload is a null pointer."); + let payload = unsafe { std::slice::from_raw_parts(payload, payload_len) }; + let mut scheduler_state = SchedulerState {}; + let mut block_state = crate::block_state::BlockState {}; + match crate::execute_transaction(&mut scheduler_state, &mut block_state, payload) { + Ok(()) => 0, + Err(crate::TransactionRejectReason) => 1, + } +} + +/// Trackes the energy remaining and some context during the execution. +struct SchedulerState {} +impl crate::SchedulerOperations for SchedulerState { + fn sender_account(&self) -> concordium_base::base::AccountIndex { + todo!() + } + + fn sender_account_address(&self) -> concordium_base::contracts_common::AccountAddress { + todo!() + } + + fn get_energy(&self) -> concordium_base::base::Energy { + todo!() + } + + fn tick_energy( + &mut self, + _energy: concordium_base::base::Energy, + ) -> Result<(), crate::OutOfEnergyError> { + todo!() + } +} diff --git a/plt/plt-scheduler/src/lib.rs b/plt/plt-scheduler/src/lib.rs new file mode 100644 index 0000000000..eb5e24222e --- /dev/null +++ b/plt/plt-scheduler/src/lib.rs @@ -0,0 +1,222 @@ +use concordium_base::base::{AccountIndex, Energy}; +use concordium_base::id::types::AccountAddress; +use concordium_base::protocol_level_tokens::TokenId; +use plt_token_module::token_kernel_interface::RawTokenAmount; + +mod block_state; +#[cfg(feature = "ffi")] +mod ffi; + +// Placeholder types to be defined or replaced with types from other crates. + +pub type MutableTokenState = (); +pub type PLTConfiguration = (); +pub type TokenAmountDelta = (); + +/// Index of the protocol-level token in the block state map of tokens. +#[derive( + Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, derive_more::Into, derive_more::From, +)] +pub struct TokenIndex { + index: u64, +} + +/// Operations on the state of a block in the chain. +/// +/// This is abstracted in a trait to allow for a testing stub. +pub trait BlockStateOperations { + // Protocol-level token state query interface. + + /// Get the [`TokenId`]s of all protocol-level tokens registered on the chain. + /// + /// If the protocol version does not support protocol-level tokens, this will return the empty + /// list. + fn get_plt_list(&self) -> impl std::iter::Iterator; + + /// Get the [`TokenIndex`] associated with a [`TokenId`] (if it exists). + /// + /// # Arguments + /// + /// - `token_index` The token index to update. + fn get_token_index(&self, token_id: TokenId) -> Option; + + /// Convert a persistent state to a mutable one that can be updated by the scheduler. + /// + /// Updates to this state will only persist in the block state using [`BlockStateOperations::set_token_state`]. + /// + /// # Arguments + /// + /// - `token_index` The index of the token to get the state from. + /// + /// # Panics + /// + /// Panics if the token identified by `token_index` does not exist. + fn get_mutable_token_state(&self, token_index: TokenIndex) -> MutableTokenState; + + /// Get the configuration of a protocol-level token. + /// + /// # Arguments + /// + /// - `token_index` The index of the token to get the config for. + /// + /// # Panics + /// + /// Panics if the token identified by `token_index` does not exist. + fn get_token_configuration(&self, token_index: TokenIndex) -> PLTConfiguration; + + /// Get the circulating supply of a protocol-level token. + /// + /// # Arguments + /// + /// - `token_index` The index of the token to get the circulating supply. + /// + /// # Panics + /// + /// Panics if the token identified by `token_index` does not exist. + fn get_token_circulating_supply(&self, token_index: TokenIndex) -> RawTokenAmount; + + /// Set the recorded total circulating supply for a protocol-level token. + /// + /// This should always be kept up-to-date with the total balance held in accounts. + /// + /// # Arguments + /// + /// - `token_index` The token index to update. + /// - `circulation_supply` The new total circulating supply for the token. + /// + /// # Panics + /// + /// Panics if the token identified by `token_index` does not exist. + fn set_token_circulating_supply( + &mut self, + token_index: TokenIndex, + circulating_supply: RawTokenAmount, + ); + + /// Create a new token with the given configuration. The initial state will be empty + /// and the initial supply will be 0. Returns the token index and the updated state. + /// + /// # Arguments + /// + /// - `configuration` The configuration for the token. + /// + /// # Preconditions + /// + /// The caller must ensure the following conditions are true, and failing to do so results in + /// undefined behavior. + /// + /// - The `token_id` of the given configuration MUST NOT already be in use by a protocol-level + /// token, i.e. `assert_eq!(s.get_token_index(configuration.token_id), None)`. + /// - The [`PLTConfiguration`] MUST be valid and in particular the 'governance_account_index' + /// MUST reference a valid account. + fn create_token(&mut self, configuration: PLTConfiguration) -> TokenIndex; + + /// Update the token balance of an account. + /// + /// # Arguments + /// + /// - `token_index` The token index to update. + /// - `account_index` The account to update. + /// - `amount_delta` The token balance delta. + /// + /// # Errors + /// + /// - [`OverflowError`] The update would overflow or underflow the token balance on the account. + /// + /// # Panics + /// + /// Panics if the token identified by `token_index` does not exist. + fn update_token_account_balance( + &mut self, + token_index: TokenIndex, + account_index: AccountIndex, + amount_delta: TokenAmountDelta, + ) -> Result<(), OverflowError>; + + /// Touch the token account. This initializes a token account state with a + /// balance of zero. This only affects an account if its state for the token + /// is empty. + /// + /// Returns `false`, if the account already contained a token account state. + /// + /// # Arguments + /// + /// - `token_index` The token index to update. + /// - `account_index` The account to update. + /// + /// # Panics + /// + /// Panics if: + /// + /// - the token identified by `token_index` does not exist. + /// - the account identified by `account_index` does not exist. + #[must_use] + fn touch_token_account(&mut self, token_index: TokenIndex, account_index: AccountIndex) + -> bool; + + /// Increment the update sequence number for Protocol Level Tokens (PLT). + /// + /// Unlike the other chain updates this is a separate function, since there is no queue associated with PLTs. + fn increment_plt_update_sequence_number(&mut self); + + /// Convert a mutable state to a persistent one and store it in the block state. + /// + /// To ensure this is future-proof, the mutable state should not be used after this call. + /// + /// # Arguments + /// + /// - `token_index` The token index to update. + /// - `mutable_token_state` The mutated state to set as the current token state. + /// + /// # Panics + /// + /// Panics if the token identified by `token_index` does not exist. + fn set_token_state(&mut self, token_index: TokenIndex, mutable_token_state: MutableTokenState); +} + +/// Operations on the scheduler state. +pub trait SchedulerOperations { + /// The account initiating the transaction. + fn sender_account(&self) -> AccountIndex; + + /// The address of the account initiating the transaction. + fn sender_account_address(&self) -> AccountAddress; + + /// Get the amount of energy remaining for the execution. + fn get_energy(&self) -> Energy; + + /// Reduce the available energy for the execution. + /// + /// # Arguments + /// + /// - `energy` The amount of energy to charge. + /// + /// # Errors + /// + /// - [`OutOfEnergyError`] If the available energy is smaller than the ticked amount. + fn tick_energy(&mut self, energy: Energy) -> Result<(), OutOfEnergyError>; +} + +/// Transaction execution ran out of energy. +#[derive(Debug)] +pub struct OutOfEnergyError; + +/// The computation resulted in overflow. +#[derive(Debug)] +pub struct OverflowError; + +pub struct TransactionRejectReason; + +pub type Events = (); + +/// Execute a transaction payload modifying `scheduler` and `block_state` accordingly. +/// Returns the events produce if successful otherwise a reject reason. +/// +/// The caller must ensure to rollback state changes in case of the transaction being rejected. +pub fn execute_transaction( + _scheduler: &mut impl SchedulerOperations, + _block_state: &mut impl BlockStateOperations, + _payload: &[u8], +) -> Result { + todo!() +} diff --git a/plt/plt-token-module/Cargo.toml b/plt/plt-token-module/Cargo.toml new file mode 100644 index 0000000000..68c1578dc0 --- /dev/null +++ b/plt/plt-token-module/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "plt-token-module" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib", "staticlib", "rlib"] + +[dependencies] +concordium_base.workspace = true +thiserror.workspace = true + +[dev-dependencies] +assert_matches.workspace = true \ No newline at end of file diff --git a/plt/plt-token-module/README.md b/plt/plt-token-module/README.md new file mode 100644 index 0000000000..1c4895cc66 --- /dev/null +++ b/plt/plt-token-module/README.md @@ -0,0 +1,8 @@ +# concordium-node: Deployment unit for Protocol-level token (PLT) + +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](https://github.com/Concordium/.github/blob/main/.github/CODE_OF_CONDUCT.md) +![Build and test](https://github.com/Concordium/concordium-node/actions/workflows/deployment-build-test.yaml/badge.svg) + +This project implements the runtime upgradable PLT deployment unit introduced as part of Concordium Protocol Version 10. + +The PLT deployment unit is written as a Rust library which is then compiled to [WebAssembly](https://webassembly.org/) and can be deployed on the Concordium blockchain by the Governance Committee. diff --git a/plt/plt-token-module/src/lib.rs b/plt/plt-token-module/src/lib.rs new file mode 100644 index 0000000000..2f56aa53ad --- /dev/null +++ b/plt/plt-token-module/src/lib.rs @@ -0,0 +1,2 @@ +pub mod token_kernel_interface; +pub mod token_module; diff --git a/plt/plt-token-module/src/token_kernel_interface.rs b/plt/plt-token-module/src/token_kernel_interface.rs new file mode 100644 index 0000000000..b575e5f42b --- /dev/null +++ b/plt/plt-token-module/src/token_kernel_interface.rs @@ -0,0 +1,151 @@ +//! Token kernel interface for protocol-level tokens. The kernel handles all operations affecting token +//! balance and supply and manages the state and events related to balances and supply. + +use concordium_base::base::{AccountIndex, Energy}; +use concordium_base::contracts_common::AccountAddress; +use concordium_base::protocol_level_tokens::TokenModuleEventType; +use concordium_base::transactions::Memo; + +pub type StateKey = Vec; +pub type StateValue = Vec; + +/// Token amount without decimals specified. The token amount represented by +/// this type must always be represented with the number of decimals +/// the token natively has. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Default)] +pub struct RawTokenAmount(pub u64); + +/// The account has insufficient balance. +#[derive(Debug)] +pub struct InsufficientBalanceError; + +/// Update to state key failed because the key was locked by an iterator. +#[derive(Debug, thiserror::Error)] +#[error("State key is locked")] +pub struct LockedStateKeyError; + +/// Mint exceed the representable amount. +#[derive(Debug, thiserror::Error)] +#[error("Amount not representable")] +pub struct AmountNotRepresentableError; + +/// Energy limit for execution reached. +#[derive(Debug, thiserror::Error)] +#[error("Out of energy")] +pub struct OutOfEnergyError; + +/// Queries provided by the token kernel. +pub trait TokenKernelQueries { + /// The type for the account object. + /// + /// The account is guaranteed to exist on chain, when holding an instance of this type. + type Account; + + /// Lookup the account using an account address. + fn account_by_address(&self, address: &AccountAddress) -> Option; + + /// Lookup the account using an account index. + fn account_by_index(&self, index: AccountIndex) -> Option; + + /// Get the account index for the account. + fn account_index(&self, account: &Self::Account) -> AccountIndex; + + /// Get the canonical account address of the account, i.e. the address used as part of the + /// credential deployment and not an alias. + fn account_canonical_address(&self, account: &Self::Account) -> AccountAddress; + + /// Get the token balance of the account. + fn account_balance(&self, account: &Self::Account) -> RawTokenAmount; + + /// The current token circulation supply. + fn circulating_supply(&self) -> RawTokenAmount; + + /// The number of decimals used in the presentation of the token amount. + fn decimals(&self) -> u8; + + /// Lookup a key in the token state. + fn get_token_state(&self, key: StateKey) -> Option; +} + +/// Operations provided by the token kernel. +pub trait TokenKernelOperations: TokenKernelQueries { + /// Update the balance of the given account to zero if it didn't have a balance before. + /// + /// Returns `true` if the balance wasn't present on the given account and `false` otherwise. + fn touch(&mut self, account: &Self::Account) -> bool; + + /// Mint a specified amount and deposit it in the account. + /// + /// # Events + /// + /// This will produce a `TokenMintEvent` in the logs. + /// + /// # Errors + /// + /// - [`AmountNotRepresentableError`] The total supply would exceed the representable amount. + fn mint( + &mut self, + account: &Self::Account, + amount: RawTokenAmount, + ) -> Result<(), AmountNotRepresentableError>; + + /// Burn a specified amount from the account. + /// + /// # Events + /// + /// This will produce a `TokenBurnEvent` in the logs. + /// + /// # Errors + /// + /// - [`InsufficientBalanceError`] The sender has insufficient balance. + fn burn( + &mut self, + account: &Self::Account, + amount: RawTokenAmount, + ) -> Result<(), InsufficientBalanceError>; + + /// Transfer a token amount from one account to another, with an optional memo. + /// + /// # Events + /// + /// This will produce a `TokenTransferEvent` in the logs. + /// + /// # Errors + /// + /// - [`InsufficientBalanceError`] The sender has insufficient balance. + fn transfer( + &mut self, + from: &Self::Account, + to: &Self::Account, + amount: RawTokenAmount, + memo: Option, + ) -> Result<(), InsufficientBalanceError>; + + /// Set or clear a value in the token state at the corresponding key. + /// + /// Returns whether there was an existing entry. + /// + /// # Errors + /// + /// - [`LockedStateKeyError`] if the update failed because the key was locked by an iterator. + fn set_token_state( + &mut self, + key: StateKey, + value: Option, + ) -> Result; + + /// Reduce the available energy for the PLT module execution. + /// + /// If the available energy is smaller than the given amount, an + /// "out of energy" error will be returned, in which case the caller + /// should stop execution and propagate the error upwards. + /// The energy is charged in any case (also in case of failure). + fn tick_energy(&mut self, energy: Energy) -> Result<(), OutOfEnergyError>; + + /// Log a token module event with the specified type and details. + /// + /// # Events + /// + /// This will produce a `TokenModuleEvent` in the logs. + fn log_token_event(&mut self, event: TokenModuleEventType); +} diff --git a/plt/plt-token-module/src/token_module.rs b/plt/plt-token-module/src/token_module.rs new file mode 100644 index 0000000000..3cfb39141e --- /dev/null +++ b/plt/plt-token-module/src/token_module.rs @@ -0,0 +1,253 @@ +//! Implementation of the protocol-level token module. +//! + +use crate::token_kernel_interface::*; +use concordium_base::common::cbor; +use concordium_base::common::cbor::{SerializationOptions, UnknownMapKeys, cbor_encode}; +use concordium_base::contracts_common::AccountAddress; +use concordium_base::protocol_level_tokens::{ + RawCbor, TokenAmount, TokenModuleInitializationParameters, +}; + +/// Extension trait for `TokenKernelOperations` to provide convenience wrappers for +/// module state access and updating. +trait KernelOperationsExt: TokenKernelOperations { + /// Set or clear a value in the token module state at the corresponding key. + fn set_module_state<'a>( + &mut self, + key: impl IntoIterator, + value: Option, + ) -> Result<(), LockedStateKeyError> { + self.set_token_state(module_state_key(key), value)?; + Ok(()) + } +} + +impl KernelOperationsExt for T {} + +/// Little-endian prefix used to distinguish module state keys. +const MODULE_STATE_PREFIX: [u8; 2] = 0u16.to_le_bytes(); + +/// Construct a [`StateKey`] for a module key. This prefixes the key to +/// distinguish it from other keys. +fn module_state_key<'a>(key: impl IntoIterator) -> StateKey { + let iter = key.into_iter(); + let mut module_key = Vec::with_capacity(MODULE_STATE_PREFIX.len() + iter.size_hint().0); + module_key.extend_from_slice(&MODULE_STATE_PREFIX); + module_key.extend(iter); + module_key +} + +/// Represents the reasons why [`initialize_token`] can fail. +#[derive(Debug, thiserror::Error)] +pub enum TokenInitializationError { + #[error("Invalid token initialization parameters: {0}")] + InvalidInitializationParameters(String), + #[error("{0}")] + LockedStateKey(#[from] LockedStateKeyError), + #[error("The given governance account does not exist: {0}")] + GovernanceAccountDoesNotExist(AccountAddress), + #[error("The initial mint amount has wrong number of decimals: {0}")] + MintAmountDecimalsMismatch(#[from] TokenAmountDecimalsMismatchError), + #[error("The initial mint amount is not representable: {0}")] + MintAmountNotRepresentable(#[from] AmountNotRepresentableError), +} + +/// Represents the reasons why [`execute_token_update_transaction`] can fail. +#[derive(Debug, thiserror::Error)] +pub enum TokenUpdateError {} + +/// Represents the reasons why a query to the token module can fail. +#[derive(Debug)] +pub enum TokenQueryError {} + +/// The context for a token-holder or token-governance transaction. +#[derive(Debug)] +pub struct TransactionContext { + /// The sender account object. + pub sender: Account, + /// The sender account address. This is the account alias that is used by the transaction itself. + pub sender_address: AccountAddress, +} + +#[derive(Debug, thiserror::Error)] +#[error("Token amount decimals mismatch: expected {expected}, found {found}")] +pub struct TokenAmountDecimalsMismatchError { + pub expected: u8, + pub found: u8, +} + +/// Asserts that token amount has the right number of decimals and converts it to a plain +/// integer. +fn to_token_raw_amount( + amount: TokenAmount, + expected_decimals: u8, +) -> Result { + let decimals = amount.decimals(); + if decimals != expected_decimals { + return Err(TokenAmountDecimalsMismatchError { + expected: expected_decimals, + found: decimals, + }); + } + Ok(RawTokenAmount(amount.value())) +} + +const STATE_KEY_NAME: &[u8] = b"name"; +const STATE_KEY_METADATA: &[u8] = b"metadata"; +const STATE_KEY_ALLOW_LIST: &[u8] = b"allowList"; +const STATE_KEY_DENY_LIST: &[u8] = b"denyList"; +const STATE_KEY_MINTABLE: &[u8] = b"mintable"; +const STATE_KEY_BURNABLE: &[u8] = b"burnable"; +const STATE_KEY_GOVERNANCE_ACCOUNT: &[u8] = b"governanceAccount"; + +/// Initialize a PLT by recording the relevant configuration parameters in the state and +/// (if necessary) minting the initial supply to the token governance account. +pub fn initialize_token( + host: &mut impl TokenKernelOperations, + initialization_parameters_cbor: RawCbor, +) -> Result<(), TokenInitializationError> { + let decode_options = SerializationOptions { + unknown_map_keys: UnknownMapKeys::Fail, + }; + let init_params: TokenModuleInitializationParameters = + cbor::cbor_decode_with_options(initialization_parameters_cbor, decode_options).map_err( + |err| { + TokenInitializationError::InvalidInitializationParameters(format!( + "Error decoding token initialization parameters: {}", + err + )) + }, + )?; + if !init_params.additional.is_empty() { + return Err(TokenInitializationError::InvalidInitializationParameters( + format!( + "Unknown additional parameters: {}", + init_params + .additional + .keys() + .map(|k| k.as_str()) + .collect::>() + .join(", ") + ), + )); + } + let name = init_params.name.ok_or_else(|| { + TokenInitializationError::InvalidInitializationParameters( + "Token name is missing".to_string(), + ) + })?; + let metadata = init_params.metadata.ok_or_else(|| { + TokenInitializationError::InvalidInitializationParameters( + "Token metadata is missing".to_string(), + ) + })?; + let governance_account = init_params.governance_account.ok_or_else(|| { + TokenInitializationError::InvalidInitializationParameters( + "Token governance account is missing".to_string(), + ) + })?; + host.set_module_state(STATE_KEY_NAME, Some(name.into()))?; + let encoded_metadata = cbor_encode(&metadata).map_err(|err| { + TokenInitializationError::InvalidInitializationParameters(format!( + "Error encoding token metadata: {}", + err + )) + })?; + host.set_module_state(STATE_KEY_METADATA, Some(encoded_metadata))?; + if init_params.allow_list == Some(true) { + host.set_module_state(STATE_KEY_ALLOW_LIST, Some(vec![]))?; + } + if init_params.deny_list == Some(true) { + host.set_module_state(STATE_KEY_DENY_LIST, Some(vec![]))?; + } + if init_params.mintable == Some(true) { + host.set_module_state(STATE_KEY_MINTABLE, Some(vec![]))?; + } + if init_params.burnable == Some(true) { + host.set_module_state(STATE_KEY_BURNABLE, Some(vec![]))?; + } + + let governance_account = host.account_by_address(&governance_account.address).ok_or( + TokenInitializationError::GovernanceAccountDoesNotExist(governance_account.address), + )?; + let governance_account_index = host.account_index(&governance_account); + host.set_module_state( + STATE_KEY_GOVERNANCE_ACCOUNT, + Some(governance_account_index.index.to_be_bytes().to_vec()), + )?; + if let Some(initial_supply) = init_params.initial_supply { + let mint_amount = to_token_raw_amount(initial_supply, host.decimals())?; + host.mint(&governance_account, mint_amount) + .map_err(TokenInitializationError::MintAmountNotRepresentable)?; + } + Ok(()) +} + +/// Execute a token update transaction using the [`TokenKernelOperations`] implementation on `host` to +/// update state and produce events. +/// +/// When resulting in an `Err` signals a rejected operation and all of the calls to +/// [`TokenKernelOperations`] must be rolled back y the caller. +/// +/// The process is as follows: +/// +/// - Decode the transaction CBOR parameter. +/// - Check that amounts are within the representable range. +/// - For each transfer operation: +/// +/// - Check that the module is not paused. +/// - Check that the recipient is valid. +/// - Check allowList/denyList restrictions. +/// - Transfer the amount from the sender to the recipient, if the sender's balance is +/// sufficient. +/// +/// - For each list update operation: +/// +/// - Check that the governance account is the sender. +/// - Check that the module configuration allows the list operation. +/// - Check that the account to add/remove exists on-chain. +/// +/// - For each mint operation: +/// +/// - Check that the governance account is the sender. +/// - Check that the module is not paused. +/// - Check that the module configuration allows minting. +/// - Check that the minting process was successful. +/// +/// - For each burn operation: +/// +/// - Check that the governance account is the sender. +/// - Check that the module is not paused. +/// - Check that the module configuration allows burning. +/// - Check that the burning process was successful. +/// +/// - For each pause/unpause operation: +/// +/// - Check that the governance account is the sender. +/// +/// # INVARIANTS: +/// +/// - Token module state contains a correctly encoded governance account address. +pub fn execute_token_update_transaction( + _kernel: &mut Kernel, + _context: TransactionContext, + _token_operations: RawCbor, +) -> Result<(), TokenUpdateError> { + todo!() +} + +/// Get the CBOR-encoded representation of the token module state. +pub fn query_token_module_state( + _kernel: &impl TokenKernelQueries, +) -> Result { + todo!() +} + +/// Get the CBOR-encoded representation of the token module account state. +pub fn query_account_state( + _kernel: &impl TokenKernelQueries, + _account: Kernel::Account, +) -> Result, TokenQueryError> { + todo!() +} diff --git a/plt/plt-token-module/tests/kernel_stub.rs b/plt/plt-token-module/tests/kernel_stub.rs new file mode 100644 index 0000000000..753fbb5084 --- /dev/null +++ b/plt/plt-token-module/tests/kernel_stub.rs @@ -0,0 +1,257 @@ +use std::collections::HashMap; + +use concordium_base::base::{AccountIndex, Energy}; +use concordium_base::contracts_common::AccountAddress; +use concordium_base::protocol_level_tokens::TokenModuleEventType; +use concordium_base::transactions::Memo; +use plt_token_module::token_kernel_interface::{ + AmountNotRepresentableError, InsufficientBalanceError, LockedStateKeyError, RawTokenAmount, + StateKey, StateValue, TokenKernelOperations, TokenKernelQueries, +}; + +/// Token kernel stub providing an implementation of [`TokenKernelOperations`] and methods for +/// configuring the state of the host. +#[derive(Debug)] +pub struct KernelStub { + /// List of accounts existing. + accounts: Vec, + /// Token managed state. + pub state: HashMap, + /// Decimal places in token representation. + decimals: u8, + next_account_index: AccountIndex, +} + +/// Internal representation of an Account in [`KernelStub`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Account { + /// The index of the account + pub index: AccountIndex, + /// The canonical account address of the account. + pub address: AccountAddress, + /// The token balance of the account. + pub balance: Option, +} + +impl KernelStub { + /// Create + pub fn new(decimals: u8) -> Self { + Self { + accounts: vec![], + state: Default::default(), + decimals, + next_account_index: AccountIndex { index: 0 }, + } + } + + /// Create an account in the stub. + /// + /// # Example + /// + /// ``` + /// let mut stub = KernelStub::new(0); + /// let account = stub.create_account(); + /// assert!(host.account_by_address(account_address1).is_some(), "Account must exist"); + /// ``` + pub fn create_account(&mut self) -> AccountStubIndex { + let index = self.next_account_index; + let mut address = AccountAddress([0u8; 32]); + address.0[..8].copy_from_slice(&index.index.to_be_bytes()); + let account = Account { + index, + address, + balance: None, + }; + let stub_index = AccountStubIndex(self.accounts.len()); + self.accounts.push(account); + + self.next_account_index.index += 1; + + stub_index + } + + /// Set account balance in the stub + pub fn set_account_balance(&mut self, account: AccountStubIndex, balance: RawTokenAmount) { + self.accounts + .get_mut(account.0) + .expect("account in stub") + .balance = Some(balance); + } +} + +/// Host stub account object. +/// +/// When testing it is the index into the list of accounts tracked by the `KernelStub`. +/// Holding +#[derive(Debug, Clone, Copy)] +pub struct AccountStubIndex(usize); + +impl TokenKernelQueries for KernelStub { + type Account = AccountStubIndex; + + fn account_by_address(&self, address: &AccountAddress) -> Option { + self.accounts.iter().enumerate().find_map(|(i, account)| { + // TODO resolve an account alias as well here. + if account.address == *address { + Some(AccountStubIndex(i)) + } else { + None + } + }) + } + + fn account_by_index(&self, index: AccountIndex) -> Option { + self.accounts.iter().enumerate().find_map(|(i, account)| { + if account.index == index { + Some(AccountStubIndex(i)) + } else { + None + } + }) + } + + fn account_index(&self, account: &Self::Account) -> AccountIndex { + self.accounts[account.0].index + } + + fn account_canonical_address(&self, account: &Self::Account) -> AccountAddress { + self.accounts[account.0].address + } + + fn account_balance(&self, account: &Self::Account) -> RawTokenAmount { + self.accounts[account.0].balance.unwrap_or_default() + } + + fn circulating_supply(&self) -> RawTokenAmount { + todo!() + } + + fn decimals(&self) -> u8 { + self.decimals + } + + fn get_token_state(&self, key: StateKey) -> Option { + self.state.get(&key).cloned() + } +} + +impl TokenKernelOperations for KernelStub { + fn touch(&mut self, account: &Self::Account) -> bool { + if self.accounts[account.0].balance.is_some() { + false + } else { + self.accounts[account.0].balance = Some(RawTokenAmount::default()); + true + } + } + + fn mint( + &mut self, + account: &Self::Account, + amount: RawTokenAmount, + ) -> Result<(), AmountNotRepresentableError> { + if let Some(balance) = self.accounts[account.0].balance { + if balance > RawTokenAmount(u64::MAX - amount.0) { + Err(AmountNotRepresentableError) + } else { + self.accounts[account.0].balance = Some(RawTokenAmount(balance.0 + amount.0)); + Ok(()) + } + } else { + self.accounts[account.0].balance = Some(amount); + Ok(()) + } + } + + fn burn( + &mut self, + _account: &Self::Account, + _amount: RawTokenAmount, + ) -> Result<(), InsufficientBalanceError> { + todo!() + } + + fn transfer( + &mut self, + _from: &Self::Account, + _to: &Self::Account, + _amount: RawTokenAmount, + _memo: Option, + ) -> Result<(), InsufficientBalanceError> { + todo!() + } + + fn set_token_state( + &mut self, + key: StateKey, + value: Option, + ) -> Result { + let res = match value { + None => self.state.remove(&key).is_some(), + Some(value) => self.state.insert(key, value).is_some(), + }; + Ok(res) + } + + fn tick_energy(&mut self, _energy: Energy) { + todo!() + } + + fn log_token_event(&mut self, _event: TokenModuleEventType) { + todo!() + } +} + +// Tests for the kernel stub + +const TEST_ACCOUNT2: AccountAddress = AccountAddress([2u8; 32]); + +/// Test lookup account address and account from address +#[test] +fn test_account_lookup_address() { + let mut stub = KernelStub::new(0); + let account = stub.create_account(); + + let address = stub.account_canonical_address(&account); + stub.account_by_address(&address) + .expect("Account is expected to exist"); + assert!( + stub.account_by_address(&TEST_ACCOUNT2).is_none(), + "Account is not expected to exist" + ); +} + +/// Test lookup account index and account from index +#[test] +fn test_account_lookup_index() { + let mut stub = KernelStub::new(0); + let account = stub.create_account(); + + let index = stub.account_index(&account); + stub.account_by_index(index) + .expect("Account is expected to exist"); + assert!( + stub.account_by_index(2.into()).is_none(), + "Account is not expected to exist" + ); +} + +/// Test get account balance +#[test] +fn test_account_balance() { + let mut stub = KernelStub::new(0); + let account0 = stub.create_account(); + let account1 = stub.create_account(); + stub.set_account_balance(account0, RawTokenAmount(245)); + + let balance = stub.account_balance(&account0); + assert_eq!(balance, RawTokenAmount(245)); + + let balance = stub.account_balance(&account1); + assert_eq!(balance, RawTokenAmount(0)); +} + +#[test] +fn test_account_lookup_canonical_address() { + // TODO test lookup using alias. +} diff --git a/plt/plt-token-module/tests/token_module_initialize.rs b/plt/plt-token-module/tests/token_module_initialize.rs new file mode 100644 index 0000000000..fc4cf8e7bb --- /dev/null +++ b/plt/plt-token-module/tests/token_module_initialize.rs @@ -0,0 +1,268 @@ +use std::collections::HashMap; + +use assert_matches::assert_matches; +use concordium_base::common::cbor; +use concordium_base::contracts_common::AccountAddress; +use concordium_base::{ + common::cbor::value::Value, + protocol_level_tokens::{TokenAmount, TokenModuleInitializationParameters}, +}; +use kernel_stub::KernelStub; +use plt_token_module::token_kernel_interface::{RawTokenAmount, TokenKernelQueries}; +use plt_token_module::token_module::{ + self, TokenAmountDecimalsMismatchError, TokenInitializationError, +}; + +mod kernel_stub; + +const TEST_ACCOUNT2: AccountAddress = AccountAddress([2u8; 32]); + +/// In this example, the parameters are not a valid encoding. +#[test] +fn test_initialize_token_parameters_decode_failure() { + let mut stub = KernelStub::new(0); + let res = token_module::initialize_token(&mut stub, vec![].into()); + assert_matches!( + &res, + Err(TokenInitializationError::InvalidInitializationParameters(err)) + if err.contains("Error decoding token initialization parameters") + ); +} + +/// In this example, a parameter is missing from the required initialization parameters. +#[test] +fn test_initialize_token_parameters_missing() { + let mut stub = KernelStub::new(0); + let gov_account = stub.create_account(); + let parameters = TokenModuleInitializationParameters { + name: None, + metadata: Some("https://plt.token".to_owned().into()), + governance_account: Some(stub.account_canonical_address(&gov_account).into()), + allow_list: Some(true), + deny_list: Some(false), + initial_supply: None, + mintable: Some(true), + burnable: Some(true), + additional: Default::default(), + }; + let encoded_parameters = cbor::cbor_encode(¶meters).unwrap().into(); + let res = token_module::initialize_token(&mut stub, encoded_parameters); + assert_matches!(res, + Err(TokenInitializationError::InvalidInitializationParameters(err)) + if err == "Token name is missing" + ); +} + +/// In this example, an unsupported additional parameter is present in the +/// initialization parameters. +#[test] +fn test_initialize_token_additional_parameter() { + let mut stub = KernelStub::new(0); + let gov_account = stub.create_account(); + let mut additional = HashMap::with_capacity(1); + additional.insert("_param1".into(), Value::Text("extravalue1".into())); + let parameters = TokenModuleInitializationParameters { + name: Some("Protocol-level token".to_owned()), + metadata: Some("https://plt.token".to_owned().into()), + governance_account: Some(stub.account_canonical_address(&gov_account).into()), + allow_list: Some(true), + deny_list: Some(false), + initial_supply: None, + mintable: Some(true), + burnable: Some(true), + additional, + }; + let encoded_parameters = cbor::cbor_encode(¶meters).unwrap().into(); + let res = token_module::initialize_token(&mut stub, encoded_parameters); + assert_matches!( + res, + Err(TokenInitializationError::InvalidInitializationParameters(err)) + if err == "Unknown additional parameters: _param1" + ); +} + +/// In this example, minimal parameters are specified to check defaulting +/// behaviour. +#[test] +fn test_initialize_token_default_values() { + let mut stub = KernelStub::new(0); + let gov_account = stub.create_account(); + let metadata = "https://plt.token".to_owned().into(); + let encoded_metadata = cbor::cbor_encode(&metadata).unwrap(); + let parameters = TokenModuleInitializationParameters { + name: Some("Protocol-level token".to_owned()), + metadata: Some(metadata), + governance_account: Some(stub.account_canonical_address(&gov_account).into()), + allow_list: None, + deny_list: None, + initial_supply: None, + mintable: None, + burnable: None, + additional: Default::default(), + }; + let encoded_parameters = cbor::cbor_encode(¶meters).unwrap().into(); + token_module::initialize_token(&mut stub, encoded_parameters).unwrap(); + let mut expected_state = HashMap::with_capacity(3); + expected_state.insert(b"\0\0name".into(), b"Protocol-level token".into()); + expected_state.insert(b"\0\0metadata".into(), encoded_metadata); + + expected_state.insert( + b"\0\0governanceAccount".into(), + stub.account_index(&gov_account).index.to_be_bytes().into(), + ); + assert_eq!(stub.state, expected_state); +} + +/// In this example, the parameters are valid, no minting. +#[test] +fn test_initialize_token_no_minting() { + let mut stub = KernelStub::new(0); + let gov_account = stub.create_account(); + let metadata = "https://plt.token".to_owned().into(); + let encoded_metadata = cbor::cbor_encode(&metadata).unwrap(); + let parameters = TokenModuleInitializationParameters { + name: Some("Protocol-level token".to_owned()), + metadata: Some(metadata), + governance_account: Some(stub.account_canonical_address(&gov_account).into()), + allow_list: Some(true), + deny_list: Some(false), + initial_supply: None, + mintable: Some(true), + burnable: Some(true), + additional: Default::default(), + }; + let encoded_parameters = cbor::cbor_encode(¶meters).unwrap().into(); + token_module::initialize_token(&mut stub, encoded_parameters).unwrap(); + let mut expected_state = HashMap::with_capacity(3); + expected_state.insert(b"\0\0name".into(), b"Protocol-level token".into()); + expected_state.insert(b"\0\0metadata".into(), encoded_metadata); + expected_state.insert( + b"\0\0governanceAccount".into(), + stub.account_index(&gov_account).index.to_be_bytes().into(), + ); + expected_state.insert(b"\0\0allowList".into(), vec![]); + expected_state.insert(b"\0\0mintable".into(), vec![]); + expected_state.insert(b"\0\0burnable".into(), vec![]); + assert_eq!(stub.state, expected_state); +} + +/// In this example, the parameters are valid, with minting. +#[test] +fn test_initialize_token_valid_2() { + let mut stub = KernelStub::new(2); + let gov_account = stub.create_account(); + let metadata = "https://plt.token".to_owned().into(); + let encoded_metadata = cbor::cbor_encode(&metadata).unwrap(); + let parameters = TokenModuleInitializationParameters { + name: Some("Protocol-level token".to_owned()), + metadata: Some(metadata), + governance_account: Some(stub.account_canonical_address(&gov_account).into()), + allow_list: Some(false), + deny_list: Some(true), + initial_supply: Some(TokenAmount::from_raw(500000, 2)), + mintable: Some(false), + burnable: Some(false), + additional: Default::default(), + }; + let encoded_parameters = cbor::cbor_encode(¶meters).unwrap().into(); + token_module::initialize_token(&mut stub, encoded_parameters).unwrap(); + assert_eq!(stub.account_balance(&gov_account), RawTokenAmount(500000)); + let mut expected_state = HashMap::with_capacity(3); + expected_state.insert(b"\0\0name".into(), b"Protocol-level token".into()); + expected_state.insert(b"\0\0metadata".into(), encoded_metadata); + expected_state.insert( + b"\0\0governanceAccount".into(), + stub.account_index(&gov_account).index.to_be_bytes().into(), + ); + expected_state.insert(b"\0\0denyList".into(), vec![]); + assert_eq!(stub.state, expected_state); +} + +/// In this example, the parameters specify an initial supply with higher precision +/// than the token allows. +#[test] +fn test_initialize_token_excessive_mint_decimals() { + let mut stub = KernelStub::new(2); + let gov_account = stub.create_account(); + let metadata = "https://plt.token".to_owned().into(); + let parameters = TokenModuleInitializationParameters { + name: Some("Protocol-level token".to_owned()), + metadata: Some(metadata), + governance_account: Some(stub.account_canonical_address(&gov_account).into()), + allow_list: Some(false), + deny_list: Some(false), + initial_supply: Some(TokenAmount::from_raw(500000, 6)), + mintable: Some(false), + burnable: Some(false), + additional: Default::default(), + }; + let encoded_parameters = cbor::cbor_encode(¶meters).unwrap().into(); + let res = token_module::initialize_token(&mut stub, encoded_parameters); + assert_matches!( + res, + Err(TokenInitializationError::MintAmountDecimalsMismatch( + TokenAmountDecimalsMismatchError { + expected: 2, + found: 6 + } + )) + ); +} + +/// In this example, the parameters specify an initial supply with less precision +/// than the token allows. +#[test] +fn test_initialize_token_insufficient_mint_decimals() { + let mut stub = KernelStub::new(6); + let gov_account = stub.create_account(); + let metadata = "https://plt.token".to_owned().into(); + let parameters = TokenModuleInitializationParameters { + name: Some("Protocol-level token".to_owned()), + metadata: Some(metadata), + governance_account: Some(stub.account_canonical_address(&gov_account).into()), + allow_list: Some(false), + deny_list: Some(false), + initial_supply: Some(TokenAmount::from_raw(500000, 2)), + mintable: Some(false), + burnable: Some(false), + additional: Default::default(), + }; + let encoded_parameters = cbor::cbor_encode(¶meters).unwrap().into(); + let res = token_module::initialize_token(&mut stub, encoded_parameters); + assert_matches!( + res, + Err(TokenInitializationError::MintAmountDecimalsMismatch( + TokenAmountDecimalsMismatchError { + expected: 6, + found: 2 + } + )) + ); +} + +/// In this example, the parameters specify an initial supply with less precision +/// than the token allows. +#[test] +fn test_initialize_token_non_existing_governance_account() { + let mut stub = KernelStub::new(0); + let metadata = "https://plt.token".to_owned().into(); + let parameters = TokenModuleInitializationParameters { + name: Some("Protocol-level token".to_owned()), + metadata: Some(metadata), + governance_account: Some(TEST_ACCOUNT2.into()), + allow_list: Some(false), + deny_list: Some(false), + initial_supply: Some(TokenAmount::from_raw(500000, 2)), + mintable: Some(false), + burnable: Some(false), + additional: Default::default(), + }; + let encoded_parameters = cbor::cbor_encode(¶meters).unwrap().into(); + let res = token_module::initialize_token(&mut stub, encoded_parameters); + assert_matches!( + res, + Err(TokenInitializationError::GovernanceAccountDoesNotExist( + TEST_ACCOUNT2 + )) + ); +} diff --git a/plt/rust-toolchain.toml b/plt/rust-toolchain.toml new file mode 100644 index 0000000000..e7f22fb8ba --- /dev/null +++ b/plt/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.86" diff --git a/scripts/distribution/windows/build-all.ps1 b/scripts/distribution/windows/build-all.ps1 index 9d87bc4aff..4e28bd0143 100644 --- a/scripts/distribution/windows/build-all.ps1 +++ b/scripts/distribution/windows/build-all.ps1 @@ -59,8 +59,9 @@ try { $filesToSign = @( "$StackInstallRoot\lib\concordium-consensus.dll", "..\..\..\concordium-base\lib\concordium_base.dll", - "..\..\..\concordium-base\smart-contracts\lib\concordium_smart_contract_engine.dll", "..\..\..\concordium-base\lib\sha_2.dll", + "..\..\..\concordium-consensus\lib\concordium_smart_contract_engine.dll", + "..\..\..\concordium-consensus\lib\plt_scheduler.dll", "..\..\..\service\windows\target\x86_64-pc-windows-msvc\release\node-runner-service.exe", "..\..\..\collector\target\release\node-collector.exe", "..\..\..\concordium-node\target\release\concordium-node.exe" diff --git a/service/windows/src/main.rs b/service/windows/src/main.rs index 8e7bd254ea..1eafc77c20 100644 --- a/service/windows/src/main.rs +++ b/service/windows/src/main.rs @@ -49,9 +49,6 @@ fn runner_service_main(arguments: Vec) { } } -/// Macro for constructing a simple status message given the new state and -/// enabled controls (if any). - /// Construct a simple status message with no enabled controls. fn simple_status(state: ServiceState) -> ServiceStatus { simple_status_with_controls(state, ServiceControlAccept::empty()) diff --git a/stack.yaml b/stack.yaml index a9aac9301c..9c0fdc4545 100644 --- a/stack.yaml +++ b/stack.yaml @@ -12,4 +12,4 @@ extra-deps: extra-lib-dirs: - ./concordium-base/lib -- ./concordium-base/smart-contracts/lib +- ./concordium-consensus/lib