diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f77a9c2b71b..38bb6687fd2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -7,10 +7,6 @@ # Changes to the genesis builder should be approved by Konstantinos or Mirko at least /crates/iota-genesis-builder/ @kodemartin @miker83z -# vm-language team -/iota-execution/ @iotaledger/vm-language -/external-crates/ @iotaledger/vm-language - # infrastructure team /docker/ @iotaledger/infrastructure @iotaledger/node @iotaledger/devops-admin /crates/iota-json-rpc*/ @iotaledger/infrastructure @@ -61,6 +57,11 @@ prettier.config.js @iotaledger/tooling turbo.json @iotaledger/tooling vercel.json @iotaledger/tooling +# vm-language team +# Needs to be after package.json ownership definition to override it +/iota-execution/ @iotaledger/vm-language +/external-crates/ @iotaledger/vm-language + # Docs and examples are for DevEx to approve upon /docs/ @iotaledger/devx /examples/ @iotaledger/devx diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7addd9967a9..cd3e0cc9b07 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,10 +46,10 @@ jobs: matrix: os: [ self-hosted, # ubuntu-x86_64 + # TODO: uncomment when runners are public available https://github.com/iotaledger/iota/issues/4421 + # ubuntu-arm64, # ubuntu-arm64 macos-latest, # macos-arm64 - # windows-latest (windows-x86_64) is disabled because we need to add custom logic for authenticating - # git for private repositories during the build (needed for iota-msim) - # MrSquaare/ssh-setup-action@v3 does not support windows + windows-latest, # windows-x86_64 ] fail-fast: false runs-on: ${{ matrix.os }} @@ -108,10 +108,10 @@ jobs: if: ${{ matrix.os == 'windows-latest' }} shell: bash run: | - choco install postgresql12 --force --params '/Password:root' - echo "C:\Program Files\PostgreSQL\12\bin" >> $GITHUB_PATH - echo "C:\Program Files\PostgreSQL\12\lib" >> $GITHUB_PATH - echo "PQ_LIB_DIR=C:\Program Files\PostgreSQL\12\lib" >> $GITHUB_ENV + choco install postgresql16 --force --params '/Password:root' + echo "C:\Program Files\PostgreSQL\16\bin" >> $GITHUB_PATH + echo "C:\Program Files\PostgreSQL\16\lib" >> $GITHUB_PATH + echo "PQ_LIB_DIR=C:\Program Files\PostgreSQL\16\lib" >> $GITHUB_ENV echo "PG_DATABASE_URL=postgres://postgres:root@localhost/" >> $GITHUB_ENV echo "PG_EXAMPLE_DATABASE_URL=postgres://postgres:root@localhost/diesel_example" >> $GITHUB_ENV @@ -126,6 +126,7 @@ jobs: run: | brew install postgresql + # TODO: uncomment when runners are public available https://github.com/iotaledger/iota/issues/4421 # NOTE: Self-hosted runners should already have postgres installed # - name: Install postgres (Ubuntu arm64) # if: ${{ matrix.os == 'ubuntu-arm64' }} @@ -227,15 +228,3 @@ jobs: # env: # # https://github.com/settings/tokens/new?scopes=public_repo,workflow # COMMITTER_TOKEN: ${{ secrets.HOMEBREW_GH_FORMULA_BUMP }} -# -# # Tag all iota images with release tag, so that they can be easily found -# tag-docker-hub-images: -# runs-on: ubuntu-latest -# steps: -# - name: Dispatch Tagging of images in DockerHub, in MystenLabs/sui-operations -# uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3.0.0 -# with: -# repository: iotaledger/iota -# token: ${{ secrets.DOCKER_BINARY_BUILDS_DISPATCH }} -# event-type: tag-docker-images -# client-payload: '{"iota_commit": "${{ github.sha }}", "repo_name": "all", "tag": "${{ env.TAG_NAME }}"}' diff --git a/Cargo.lock b/Cargo.lock index a6985400c04..272c5b18d44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6630,6 +6630,7 @@ dependencies = [ "bigdecimal", "camino", "clap", + "csv", "fastcrypto", "flate2", "fs_extra", diff --git a/apps/apps-backend/src/features/features.controller.ts b/apps/apps-backend/src/features/features.controller.ts index 5a83f185621..c3947236ec5 100644 --- a/apps/apps-backend/src/features/features.controller.ts +++ b/apps/apps-backend/src/features/features.controller.ts @@ -65,7 +65,7 @@ export class FeaturesController { defaultValue: false, }, [Feature.StardustMigration]: { - defaultValue: false, + defaultValue: true, }, [Feature.SupplyIncreaseVesting]: { defaultValue: true, diff --git a/apps/core/src/components/stake/StakedCard.tsx b/apps/core/src/components/stake/StakedCard.tsx index dad5c6f091a..58bc5c82f20 100644 --- a/apps/core/src/components/stake/StakedCard.tsx +++ b/apps/core/src/components/stake/StakedCard.tsx @@ -7,27 +7,10 @@ import { Card, CardImage, CardType, CardBody, CardAction, CardActionType } from import { useMemo } from 'react'; import { useIotaClientQuery } from '@iota/dapp-kit'; import { ImageIcon } from '../icon'; -import { determineCountDownText, ExtendedDelegatedStake } from '../../utils'; -import { TimeUnit, useFormatCoin, useGetTimeBeforeEpochNumber, useTimeAgo } from '../../hooks'; -import { NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE } from '../../constants'; +import { ExtendedDelegatedStake } from '../../utils'; +import { useFormatCoin, useStakeRewardStatus } from '../../hooks'; import React from 'react'; -export enum StakeState { - WarmUp = 'WARM_UP', - Earning = 'EARNING', - CoolDown = 'COOL_DOWN', - Withdraw = 'WITHDRAW', - InActive = 'IN_ACTIVE', -} - -const STATUS_COPY: { [key in StakeState]: string } = { - [StakeState.WarmUp]: 'Starts Earning', - [StakeState.Earning]: 'Staking Rewards', - [StakeState.CoolDown]: 'Available to withdraw', - [StakeState.Withdraw]: 'Withdraw', - [StakeState.InActive]: 'Inactive', -}; - interface StakedCardProps { extendedStake: ExtendedDelegatedStake; currentEpoch: number; @@ -45,48 +28,20 @@ export function StakedCard({ }: StakedCardProps) { const { principal, stakeRequestEpoch, estimatedReward, validatorAddress } = extendedStake; - // TODO: Once two step withdraw is available, add cool down and withdraw now logic - // For cool down epoch, show Available to withdraw add rewards to principal - // Reward earning epoch is 2 epochs after stake request epoch - const earningRewardsEpoch = - Number(stakeRequestEpoch) + NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE; - const isEarnedRewards = currentEpoch >= Number(earningRewardsEpoch); - const delegationState = inactiveValidator - ? StakeState.InActive - : isEarnedRewards - ? StakeState.Earning - : StakeState.WarmUp; - - const rewards = isEarnedRewards && estimatedReward ? BigInt(estimatedReward) : 0n; + const { rewards, title, subtitle } = useStakeRewardStatus({ + stakeRequestEpoch, + currentEpoch, + estimatedReward, + inactiveValidator, + }); // For inactive validator, show principal + rewards const [principalStaked, symbol] = useFormatCoin( inactiveValidator ? principal + rewards : principal, IOTA_TYPE_ARG, ); - const [rewardsStaked] = useFormatCoin(rewards, IOTA_TYPE_ARG); - - // Applicable only for warm up - const epochBeforeRewards = delegationState === StakeState.WarmUp ? earningRewardsEpoch : null; - - const statusText = { - // Epoch time before earning - [StakeState.WarmUp]: `Epoch #${earningRewardsEpoch}`, - [StakeState.Earning]: `${rewardsStaked} ${symbol}`, - // Epoch time before redrawing - [StakeState.CoolDown]: `Epoch #`, - [StakeState.Withdraw]: 'Now', - [StakeState.InActive]: 'Not earning rewards', - }; const { data } = useIotaClientQuery('getLatestIotaSystemState'); - const { data: rewardEpochTime } = useGetTimeBeforeEpochNumber(Number(epochBeforeRewards) || 0); - const timeAgo = useTimeAgo({ - timeFrom: rewardEpochTime || null, - shortedTimeLabel: false, - shouldEnd: true, - maxTimeUnit: TimeUnit.ONE_HOUR, - }); const validatorMeta = useMemo(() => { if (!data) return null; @@ -97,17 +52,6 @@ export function StakedCard({ ); }, [validatorAddress, data]); - const rewardTime = () => { - if (Number(epochBeforeRewards) && rewardEpochTime > 0) { - return determineCountDownText({ - timeAgo, - label: 'in', - }); - } - - return statusText[delegationState]; - }; - return ( @@ -118,11 +62,7 @@ export function StakedCard({ /> - + ); } diff --git a/apps/core/src/hooks/index.ts b/apps/core/src/hooks/index.ts index 122b9b01294..89aced3f57f 100644 --- a/apps/core/src/hooks/index.ts +++ b/apps/core/src/hooks/index.ts @@ -46,5 +46,6 @@ export * from './useNFTBasicData'; export * from './useOwnedNFT'; export * from './useNftDetails'; export * from './useCountdownByTimestamp'; +export * from './useStakeRewardStatus'; export * from './stake'; diff --git a/apps/core/src/hooks/useStakeRewardStatus.ts b/apps/core/src/hooks/useStakeRewardStatus.ts new file mode 100644 index 00000000000..415b3efc5ac --- /dev/null +++ b/apps/core/src/hooks/useStakeRewardStatus.ts @@ -0,0 +1,92 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE } from '../constants'; +import { useFormatCoin, useGetTimeBeforeEpochNumber, useTimeAgo, TimeUnit } from '.'; +import { determineCountDownText } from '../utils'; + +export function useStakeRewardStatus({ + stakeRequestEpoch, + currentEpoch, + inactiveValidator, + estimatedReward, +}: { + stakeRequestEpoch: string; + currentEpoch: number; + inactiveValidator: boolean; + estimatedReward?: string | number | bigint; +}) { + // TODO: Once two step withdraw is available, add cool down and withdraw now logic + // For cool down epoch, show Available to withdraw add rewards to principal + // Reward earning epoch is 2 epochs after stake request epoch + const earningRewardsEpoch = + Number(stakeRequestEpoch) + NUM_OF_EPOCH_BEFORE_STAKING_REWARDS_REDEEMABLE; + + const isEarnedRewards = currentEpoch >= Number(earningRewardsEpoch); + + const delegationState = inactiveValidator + ? StakeState.InActive + : isEarnedRewards + ? StakeState.Earning + : StakeState.WarmUp; + + const rewards = isEarnedRewards && estimatedReward ? BigInt(estimatedReward) : 0n; + + const [rewardsStaked, symbol] = useFormatCoin(rewards, IOTA_TYPE_ARG); + + // Applicable only for warm up + const epochBeforeRewards = delegationState === StakeState.WarmUp ? earningRewardsEpoch : null; + + const statusText = { + // Epoch time before earning + [StakeState.WarmUp]: `Epoch #${earningRewardsEpoch}`, + [StakeState.Earning]: `${rewardsStaked} ${symbol}`, + // Epoch time before redrawing + [StakeState.CoolDown]: `Epoch #`, + [StakeState.Withdraw]: 'Now', + [StakeState.InActive]: 'Not earning rewards', + }; + + const { data: rewardEpochTime } = useGetTimeBeforeEpochNumber(Number(epochBeforeRewards) || 0); + + const timeAgo = useTimeAgo({ + timeFrom: rewardEpochTime || null, + shortedTimeLabel: false, + shouldEnd: true, + maxTimeUnit: TimeUnit.ONE_HOUR, + }); + + const rewardTime = () => { + if (Number(epochBeforeRewards) && rewardEpochTime > 0) { + return determineCountDownText({ + timeAgo, + label: 'in', + }); + } + + return statusText[delegationState]; + }; + + return { + rewards, + title: rewardTime(), + subtitle: STATUS_COPY[delegationState], + }; +} +export enum StakeState { + WarmUp = 'WARM_UP', + Earning = 'EARNING', + CoolDown = 'COOL_DOWN', + Withdraw = 'WITHDRAW', + InActive = 'IN_ACTIVE', +} +export const STATUS_COPY: { + [key in StakeState]: string; +} = { + [StakeState.WarmUp]: 'Starts Earning', + [StakeState.Earning]: 'Staking Rewards', + [StakeState.CoolDown]: 'Available to withdraw', + [StakeState.Withdraw]: 'Withdraw', + [StakeState.InActive]: 'Inactive', +}; diff --git a/apps/explorer/package.json b/apps/explorer/package.json index 7b26ec2aff6..8982b0a9d72 100644 --- a/apps/explorer/package.json +++ b/apps/explorer/package.json @@ -86,7 +86,7 @@ "@vitejs/plugin-react": "^4.3.1", "@vitest/ui": "^0.33.0", "autoprefixer": "^10.4.19", - "happy-dom": "^10.5.1", + "happy-dom": "^15.11.7", "onchange": "^7.1.0", "postcss": "^8.4.31", "start-server-and-test": "^2.0.0", diff --git a/apps/wallet-dashboard/app/(protected)/home/page.tsx b/apps/wallet-dashboard/app/(protected)/home/page.tsx index 61ba1487213..61f55475702 100644 --- a/apps/wallet-dashboard/app/(protected)/home/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/home/page.tsx @@ -2,44 +2,35 @@ // SPDX-License-Identifier: Apache-2.0 'use client'; -import { AccountBalance, MyCoins, TransactionsOverview, StakingOverview } from '@/components'; +import { + AccountBalance, + MyCoins, + TransactionsOverview, + StakingOverview, + MigrationOverview, +} from '@/components'; import { useFeature } from '@growthbook/growthbook-react'; import { Feature } from '@iota/core'; import { useCurrentAccount, useCurrentWallet } from '@iota/dapp-kit'; -import clsx from 'clsx'; function HomeDashboardPage(): JSX.Element { const { connectionStatus } = useCurrentWallet(); const account = useCurrentAccount(); const stardustMigrationEnabled = useFeature(Feature.StardustMigration).value; - // Add the logic here to check if the user has migration objects. - const needsMigration = false && stardustMigrationEnabled; return (
{connectionStatus === 'connected' && account && ( <> -
+
- {needsMigration && ( -
- Migration -
- )} + {stardustMigrationEnabled && }
diff --git a/apps/wallet-dashboard/app/(protected)/layout.tsx b/apps/wallet-dashboard/app/(protected)/layout.tsx index 8d42d2ff757..a77c8922ab9 100644 --- a/apps/wallet-dashboard/app/(protected)/layout.tsx +++ b/apps/wallet-dashboard/app/(protected)/layout.tsx @@ -25,11 +25,14 @@ function DashboardLayout({ children }: PropsWithChildren): JSX.Element {
-
-
- + {/* This padding need to have aligned left/right content's position, because of sidebar overlap on the small screens */} +
+
+
+ +
+
{children}
-
{children}
diff --git a/apps/wallet-dashboard/app/(protected)/migrations/page.tsx b/apps/wallet-dashboard/app/(protected)/migrations/page.tsx index 55ac40f5e99..544f352e963 100644 --- a/apps/wallet-dashboard/app/(protected)/migrations/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/migrations/page.tsx @@ -2,19 +2,32 @@ // SPDX-License-Identifier: Apache-2.0 'use client'; -import { VirtualList } from '@/components'; import MigratePopup from '@/components/Popup/Popups/MigratePopup'; -import { useGetCurrentEpochStartTimestamp, usePopups } from '@/hooks'; -import { groupStardustObjectsByMigrationStatus } from '@/lib/utils'; -import { Button } from '@iota/apps-ui-kit'; -import { useCurrentAccount, useIotaClient, useIotaClientContext } from '@iota/dapp-kit'; +import { usePopups } from '@/hooks'; +import { summarizeMigratableObjectValues } from '@/lib/utils'; import { - STARDUST_BASIC_OUTPUT_TYPE, - STARDUST_NFT_OUTPUT_TYPE, - useGetAllOwnedObjects, -} from '@iota/core'; -import { getNetwork, IotaObjectData } from '@iota/iota-sdk/client'; + Button, + ButtonSize, + ButtonType, + Card, + CardBody, + CardImage, + ImageShape, + Panel, + Title, +} from '@iota/apps-ui-kit'; +import { useCurrentAccount, useIotaClient } from '@iota/dapp-kit'; +import { STARDUST_BASIC_OUTPUT_TYPE, STARDUST_NFT_OUTPUT_TYPE, useFormatCoin } from '@iota/core'; +import { useGetStardustMigratableObjects } from '@/hooks'; import { useQueryClient } from '@tanstack/react-query'; +import { Assets, Clock, IotaLogoMark, Tokens } from '@iota/ui-icons'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; + +interface MigrationDisplayCard { + title: string; + subtitle: string; + icon: React.FC; +} function MigrationDashboardPage(): JSX.Element { const account = useCurrentAccount(); @@ -22,40 +35,17 @@ function MigrationDashboardPage(): JSX.Element { const { openPopup, closePopup } = usePopups(); const queryClient = useQueryClient(); const iotaClient = useIotaClient(); - const { network } = useIotaClientContext(); - const { explorer } = getNetwork(network); - const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp(); - - const { data: basicOutputObjects } = useGetAllOwnedObjects(address, { - StructType: STARDUST_BASIC_OUTPUT_TYPE, - }); - const { data: nftOutputObjects } = useGetAllOwnedObjects(address, { - StructType: STARDUST_NFT_OUTPUT_TYPE, - }); - const { migratable: migratableBasicOutputs, unmigratable: unmigratableBasicOutputs } = - groupStardustObjectsByMigrationStatus( - basicOutputObjects ?? [], - Number(currentEpochMs), - address, - ); - - const { migratable: migratableNftOutputs, unmigratable: unmigratableNftOutputs } = - groupStardustObjectsByMigrationStatus( - nftOutputObjects ?? [], - Number(currentEpochMs), - address, - ); + const { + migratableBasicOutputs, + unmigratableBasicOutputs, + migratableNftOutputs, + unmigratableNftOutputs, + } = useGetStardustMigratableObjects(address); const hasMigratableObjects = migratableBasicOutputs.length > 0 || migratableNftOutputs.length > 0; - const virtualItem = (asset: IotaObjectData): JSX.Element => ( - - {asset.objectId} - - ); - function handleOnSuccess(digest: string): void { iotaClient .waitForTransaction({ @@ -93,41 +83,93 @@ function MigrationDashboardPage(): JSX.Element { ); } + const { + accumulatedIotaAmount: accumulatedTimelockedIotaAmount, + totalNativeTokens, + totalVisualAssets, + } = summarizeMigratableObjectValues({ + migratableBasicOutputs, + migratableNftOutputs, + address, + }); + + const [timelockedIotaTokens, symbol] = useFormatCoin( + accumulatedTimelockedIotaAmount, + IOTA_TYPE_ARG, + ); + + const MIGRATION_CARDS: MigrationDisplayCard[] = [ + { + title: `${timelockedIotaTokens} ${symbol}`, + subtitle: 'IOTA Tokens', + icon: IotaLogoMark, + }, + { + title: `${totalNativeTokens}`, + subtitle: 'Native Tokens', + icon: Tokens, + }, + { + title: `${totalVisualAssets}`, + subtitle: 'Visual Assets', + icon: Assets, + }, + ]; + + const timelockedAssetsAmount = unmigratableBasicOutputs.length + unmigratableNftOutputs.length; + const TIMELOCKED_ASSETS_CARDS: MigrationDisplayCard[] = [ + { + title: `${timelockedAssetsAmount}`, + subtitle: 'Time-locked', + icon: Clock, + }, + ]; + return (
-
-

Migratable Basic Outputs: {migratableBasicOutputs.length}

- 30} - render={virtualItem} - /> -
-
-

Unmigratable Basic Outputs: {unmigratableBasicOutputs.length}

- 30} - render={virtualItem} - /> -
-
-

Migratable NFT Outputs: {migratableNftOutputs.length}

- 30} - render={virtualItem} - /> -
-
-

Unmigratable NFT Outputs: {unmigratableNftOutputs.length}

- 30} - render={virtualItem} - /> +
+
+ + + } + /> + <div className="flex flex-col gap-xs p-md--rs"> + {MIGRATION_CARDS.map((card) => ( + <Card key={card.subtitle}> + <CardImage shape={ImageShape.SquareRounded}> + <card.icon /> + </CardImage> + <CardBody title={card.title} subtitle={card.subtitle} /> + </Card> + ))} + <Button text="See All" type={ButtonType.Ghost} fullWidth /> + </div> + </Panel> + + <Panel> + <Title title="Time-locked Assets" /> + <div className="flex flex-col gap-xs p-md--rs"> + {TIMELOCKED_ASSETS_CARDS.map((card) => ( + <Card key={card.subtitle}> + <CardImage shape={ImageShape.SquareRounded}> + <card.icon /> + </CardImage> + <CardBody title={card.title} subtitle={card.subtitle} /> + </Card> + ))} + <Button text="See All" type={ButtonType.Ghost} fullWidth /> + </div> + </Panel> + </div> </div> - <Button text="Migrate" disabled={!hasMigratableObjects} onClick={openMigratePopup} /> </div> ); } diff --git a/apps/wallet-dashboard/app/(protected)/vesting/page.tsx b/apps/wallet-dashboard/app/(protected)/vesting/page.tsx index 0488681830d..b925d57e97c 100644 --- a/apps/wallet-dashboard/app/(protected)/vesting/page.tsx +++ b/apps/wallet-dashboard/app/(protected)/vesting/page.tsx @@ -6,6 +6,7 @@ import { Banner, StakeDialog, + StakeDialogView, TimelockedUnstakePopup, useStakeDialog, VestingScheduleDialog, @@ -37,8 +38,9 @@ import { CardType, ImageType, ImageShape, - ButtonType, Button, + ButtonType, + LoadingIndicator, } from '@iota/apps-ui-kit'; import { Theme, @@ -52,19 +54,26 @@ import { useCountdownByTimestamp, Feature, } from '@iota/core'; -import { useCurrentAccount, useIotaClient, useSignAndExecuteTransaction } from '@iota/dapp-kit'; +import { + useCurrentAccount, + useIotaClient, + useIotaClientQuery, + useSignAndExecuteTransaction, +} from '@iota/dapp-kit'; import { IotaValidatorSummary } from '@iota/iota-sdk/client'; import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; import { Calendar, StarHex } from '@iota/ui-icons'; import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; +import { StakedTimelockObject } from '@/components'; function VestingDashboardPage(): JSX.Element { const account = useCurrentAccount(); const queryClient = useQueryClient(); const iotaClient = useIotaClient(); const router = useRouter(); + const { data: system } = useIotaClientQuery('getLatestIotaSystemState'); const [isVestingScheduleDialogOpen, setIsVestingScheduleDialogOpen] = useState(false); const { addNotification } = useNotifications(); const { openPopup, closePopup } = usePopups(); @@ -73,7 +82,10 @@ function VestingDashboardPage(): JSX.Element { const { data: timelockedObjects } = useGetAllOwnedObjects(account?.address || '', { StructType: TIMELOCK_IOTA_TYPE, }); - const { data: timelockedStakedObjects } = useGetTimelockedStakedObjects(account?.address || ''); + + const { data: timelockedStakedObjects, isLoading: istimelockedStakedObjectsLoading } = + useGetTimelockedStakedObjects(account?.address || ''); + const { mutateAsync: signAndExecuteTransaction } = useSignAndExecuteTransaction(); const { theme } = useTheme(); @@ -151,6 +163,16 @@ function VestingDashboardPage(): JSX.Element { ); } + const [totalStakedFormatted, totalStakedSymbol] = useFormatCoin( + vestingSchedule.totalStaked, + IOTA_TYPE_ARG, + ); + + const [totalEarnedFormatted, totalEarnedSymbol] = useFormatCoin( + vestingSchedule.totalEarned, + IOTA_TYPE_ARG, + ); + const unlockedTimelockedObjects = timelockedMapped?.filter((timelockedObject) => isTimelockedUnlockable(timelockedObject, Number(currentEpochMs)), ); @@ -232,75 +254,85 @@ function VestingDashboardPage(): JSX.Element { router.push('/'); } }, [router, supplyIncreaseVestingEnabled]); + + if (istimelockedStakedObjectsLoading) { + return ( + <div className="flex w-full max-w-4xl items-start justify-center justify-self-center"> + <LoadingIndicator /> + </div> + ); + } + return ( - <div className="flex w-full max-w-xl flex-col gap-lg justify-self-center"> - <Panel> - <Title title="Vesting" size={TitleSize.Medium} /> - <div className="flex flex-col gap-md p-lg pt-sm"> - <div className="flex h-24 flex-row gap-4"> - <DisplayStats - label="Total Vested" - value={formattedTotalVested} - supportingLabel={vestedSymbol} - /> - <DisplayStats - label="Total Locked" - value={formattedTotalLocked} - supportingLabel={lockedSymbol} - tooltipText="Total amount of IOTA that is still locked in your account." - tooltipPosition={TooltipPosition.Right} - /> + <div className="flex w-full max-w-4xl flex-col items-stretch justify-center gap-lg justify-self-center md:flex-row"> + <div className="flex w-full flex-col gap-lg md:w-1/2"> + <Panel> + <Title title="Vesting" size={TitleSize.Medium} /> + <div className="flex flex-col gap-md p-lg pt-sm"> + <div className="flex h-24 flex-row gap-4"> + <DisplayStats + label="Total Vested" + value={formattedTotalVested} + supportingLabel={vestedSymbol} + /> + <DisplayStats + label="Total Locked" + value={formattedTotalLocked} + supportingLabel={lockedSymbol} + tooltipText="Total amount of IOTA that is still locked in your account." + tooltipPosition={TooltipPosition.Right} + /> + </div> + <Card type={CardType.Outlined}> + <CardImage type={ImageType.BgSolid} shape={ImageShape.SquareRounded}> + <StarHex className="h-5 w-5 text-primary-30 dark:text-primary-80" /> + </CardImage> + <CardBody + title={`${formattedAvailableClaiming} ${availableClaimingSymbol}`} + subtitle="Available Rewards" + /> + <CardAction + type={CardActionType.Button} + onClick={handleCollect} + title="Collect" + buttonType={ButtonType.Primary} + buttonDisabled={ + !vestingSchedule.availableClaiming || + vestingSchedule.availableClaiming === 0n + } + /> + </Card> + <Card type={CardType.Outlined}> + <CardImage type={ImageType.BgSolid} shape={ImageShape.SquareRounded}> + <Calendar className="h-5 w-5 text-primary-30 dark:text-primary-80" /> + </CardImage> + <CardBody + title={`${formattedNextPayout} ${nextPayoutSymbol}`} + subtitle={`Next payout ${ + nextPayout?.expirationTimestampMs + ? formattedLastPayoutExpirationTime + : '' + }`} + /> + <CardAction + type={CardActionType.Button} + onClick={openReceiveTokenPopup} + title="See All" + buttonType={ButtonType.Secondary} + buttonDisabled={!vestingPortfolio} + /> + </Card> + {vestingPortfolio && ( + <VestingScheduleDialog + open={isVestingScheduleDialogOpen} + setOpen={setIsVestingScheduleDialogOpen} + vestingPortfolio={vestingPortfolio} + /> + )} </div> - <Card type={CardType.Outlined}> - <CardImage type={ImageType.BgSolid} shape={ImageShape.SquareRounded}> - <StarHex className="h-5 w-5 text-primary-30 dark:text-primary-80" /> - </CardImage> - <CardBody - title={`${formattedAvailableClaiming} ${availableClaimingSymbol}`} - subtitle="Available Rewards" - /> - <CardAction - type={CardActionType.Button} - onClick={handleCollect} - title="Collect" - buttonType={ButtonType.Primary} - buttonDisabled={ - !vestingSchedule.availableClaiming || - vestingSchedule.availableClaiming === 0 - } - /> - </Card> - <Card type={CardType.Outlined}> - <CardImage type={ImageType.BgSolid} shape={ImageShape.SquareRounded}> - <Calendar className="h-5 w-5 text-primary-30 dark:text-primary-80" /> - </CardImage> - <CardBody - title={`${formattedNextPayout} ${nextPayoutSymbol}`} - subtitle={`Next payout ${ - nextPayout?.expirationTimestampMs - ? formattedLastPayoutExpirationTime - : '' - }`} - /> - <CardAction - type={CardActionType.Button} - onClick={openReceiveTokenPopup} - title="See All" - buttonType={ButtonType.Secondary} - buttonDisabled={!vestingPortfolio} - /> - </Card> - {vestingPortfolio && ( - <VestingScheduleDialog - open={isVestingScheduleDialogOpen} - setOpen={setIsVestingScheduleDialogOpen} - vestingPortfolio={vestingPortfolio} - /> - )} - </div> - </Panel> - {timelockedstakedMapped.length === 0 ? ( - <> + </Panel> + + {timelockedstakedMapped.length === 0 ? ( <Banner videoSrc={videoSrc} title="Stake Vested Tokens" @@ -308,56 +340,64 @@ function VestingDashboardPage(): JSX.Element { onButtonClick={() => handleNewStake()} buttonText="Stake" /> - </> - ) : ( - <div className="flex w-1/2 flex-col items-center justify-center space-y-4 pt-12"> - <h1>Staked Vesting</h1> - <div className="flex flex-row space-x-4"> - <div className="flex flex-col items-center rounded-lg border p-4"> - <span>Your stake</span> - <span>{vestingSchedule.totalStaked}</span> + ) : null} + </div> + + {timelockedstakedMapped.length !== 0 ? ( + <div className="flex w-full md:w-1/2"> + <Panel> + <Title + title="Staked Vesting" + trailingElement={ + <Button + type={ButtonType.Primary} + text="Stake" + onClick={() => { + setStakeDialogView(StakeDialogView.SelectValidator); + }} + /> + } + /> + + <div className="flex flex-col px-lg py-sm"> + <div className="flex flex-row gap-md"> + <DisplayStats + label="Your stake" + value={`${totalStakedFormatted} ${totalStakedSymbol}`} + /> + <DisplayStats + label="Earned" + value={`${totalEarnedFormatted} ${totalEarnedSymbol}`} + /> + </div> </div> - <div className="flex flex-col items-center rounded-lg border p-4"> - <span>Total Unlocked</span> - <span>{vestingSchedule.totalUnlocked}</span> + <div className="flex flex-col px-lg py-sm"> + <div className="flex w-full flex-col items-center justify-center space-y-4 pt-4"> + {system && + timelockedStakedObjectsGrouped?.map( + (timelockedStakedObject) => { + return ( + <StakedTimelockObject + key={ + timelockedStakedObject.validatorAddress + + timelockedStakedObject.stakeRequestEpoch + + timelockedStakedObject.label + } + getValidatorByAddress={getValidatorByAddress} + timelockedStakedObject={timelockedStakedObject} + handleUnstake={handleUnstake} + currentEpoch={Number(system.epoch)} + /> + ); + }, + )} + </div> </div> - </div> - <div className="flex w-full flex-col items-center justify-center space-y-4 pt-4"> - {timelockedStakedObjectsGrouped?.map((timelockedStakedObject) => { - return ( - <div - key={ - timelockedStakedObject.validatorAddress + - timelockedStakedObject.stakeRequestEpoch + - timelockedStakedObject.label - } - className="flex w-full flex-row items-center justify-center space-x-4" - > - <span> - Validator:{' '} - {getValidatorByAddress( - timelockedStakedObject.validatorAddress, - )?.name || timelockedStakedObject.validatorAddress} - </span> - <span> - Stake Request Epoch:{' '} - {timelockedStakedObject.stakeRequestEpoch} - </span> - <span>Stakes: {timelockedStakedObject.stakes.length}</span> - - <Button - onClick={() => handleUnstake(timelockedStakedObject)} - text="Unstake" - /> - </div> - ); - })} - </div> - <Button onClick={() => handleNewStake()} text="Stake" /> + </Panel> </div> - )} + ) : null} <StakeDialog - isTimelockedStaking={true} + isTimelockedStaking stakedDetails={selectedStake} onSuccess={handleOnSuccess} isOpen={isDialogStakeOpen} diff --git a/apps/wallet-dashboard/app/globals.css b/apps/wallet-dashboard/app/globals.css index 82c7527e111..04c6811f389 100644 --- a/apps/wallet-dashboard/app/globals.css +++ b/apps/wallet-dashboard/app/globals.css @@ -36,7 +36,7 @@ body { height: 200px; } } - .home-page-grid-container.with-migration { + .home-page-grid-container:has(.with-migration) { grid-template-areas: 'balance' 'staking' @@ -56,7 +56,7 @@ body { 'vesting vesting' 'activity activity'; } - .home-page-grid-container.with-migration { + .home-page-grid-container:has(.with-migration) { grid-template-areas: 'balance balance' 'staking migration' @@ -74,7 +74,7 @@ body { 'coins vesting vesting' 'coins activity activity'; } - .home-page-grid-container.with-migration { + .home-page-grid-container:has(.with-migration) { grid-template-areas: 'balance staking migration' 'coins vesting vesting' diff --git a/apps/wallet-dashboard/app/page.tsx b/apps/wallet-dashboard/app/page.tsx index c96c01faec9..8901eec400a 100644 --- a/apps/wallet-dashboard/app/page.tsx +++ b/apps/wallet-dashboard/app/page.tsx @@ -3,17 +3,21 @@ 'use client'; -import { ConnectButton, useCurrentAccount, useCurrentWallet } from '@iota/dapp-kit'; -import { useEffect } from 'react'; +import { ConnectButton, useCurrentWallet, useAutoConnectWallet } from '@iota/dapp-kit'; import { redirect } from 'next/navigation'; import { IotaLogoWeb } from '@iota/ui-icons'; import { HOMEPAGE_ROUTE } from '@/lib/constants/routes.constants'; import { Theme, useTheme } from '@iota/core'; +import { LoadingIndicator } from '@iota/apps-ui-kit'; function HomeDashboardPage(): JSX.Element { const { theme } = useTheme(); - const { connectionStatus } = useCurrentWallet(); - const account = useCurrentAccount(); + const { isConnected } = useCurrentWallet(); + const autoConnect = useAutoConnectWallet(); + + if (isConnected) { + redirect(HOMEPAGE_ROUTE.path); + } const CURRENT_YEAR = new Date().getFullYear(); const videoSrc = @@ -21,45 +25,47 @@ function HomeDashboardPage(): JSX.Element { ? 'https://files.iota.org/media/tooling/wallet-dashboard-welcome-dark.mp4' : 'https://files.iota.org/media/tooling/wallet-dashboard-welcome-light.mp4'; - useEffect(() => { - if (connectionStatus === 'connected' && account) { - redirect(HOMEPAGE_ROUTE.path); - } - }, [connectionStatus, account]); - return ( <main className="flex h-screen"> - <div className="relative hidden sm:flex md:w-1/3"> - <video - key={theme} - src={videoSrc} - autoPlay - muted - loop - className="absolute right-0 top-0 h-full w-full min-w-fit object-cover" - disableRemotePlayback - ></video> - </div> - <div className="flex h-full w-full flex-col items-center justify-between p-md sm:p-2xl"> - <IotaLogoWeb width={130} height={32} /> - <div className="flex max-w-sm flex-col items-center gap-8 text-center"> - <div className="flex flex-col items-center gap-4"> - <span className="text-headline-sm text-neutral-40">Welcome to</span> - <h1 className="text-display-lg text-neutral-10 dark:text-neutral-100"> - IOTA Wallet - </h1> - <span className="text-title-lg text-neutral-40"> - Connecting you to the decentralized web and IOTA network - </span> + {autoConnect === 'idle' ? ( + <div className="flex w-full justify-center"> + <LoadingIndicator size="w-16 h-16" /> + </div> + ) : ( + <> + <div className="relative hidden sm:flex md:w-1/3"> + <video + key={theme} + src={videoSrc} + autoPlay + muted + loop + className="absolute right-0 top-0 h-full w-full min-w-fit object-cover" + disableRemotePlayback + ></video> </div> - <div className="[&_button]:!bg-neutral-90 [&_button]:dark:!bg-neutral-20"> - <ConnectButton connectText="Connect" /> + <div className="flex h-full w-full flex-col items-center justify-between p-md sm:p-2xl"> + <IotaLogoWeb width={130} height={32} /> + <div className="flex max-w-sm flex-col items-center gap-8 text-center"> + <div className="flex flex-col items-center gap-4"> + <span className="text-headline-sm text-neutral-40">Welcome to</span> + <h1 className="text-display-lg text-neutral-10 dark:text-neutral-100"> + IOTA Wallet + </h1> + <span className="text-title-lg text-neutral-40"> + Connecting you to the decentralized web and IOTA network + </span> + </div> + <div className="[&_button]:!bg-neutral-90 [&_button]:dark:!bg-neutral-20"> + <ConnectButton connectText="Connect" /> + </div> + </div> + <div className="text-body-lg text-neutral-60"> + © IOTA Foundation {CURRENT_YEAR} + </div> </div> - </div> - <div className="text-body-lg text-neutral-60"> - © IOTA Foundation {CURRENT_YEAR} - </div> - </div> + </> + )} </main> ); } diff --git a/apps/wallet-dashboard/components/Dialogs/SendToken/views/ReviewValuesFormView.tsx b/apps/wallet-dashboard/components/Dialogs/SendToken/views/ReviewValuesFormView.tsx index f278a637044..58d6e21e10b 100644 --- a/apps/wallet-dashboard/components/Dialogs/SendToken/views/ReviewValuesFormView.tsx +++ b/apps/wallet-dashboard/components/Dialogs/SendToken/views/ReviewValuesFormView.tsx @@ -53,9 +53,8 @@ export function ReviewValuesFormView({ <CardImage type={ImageType.BgSolid}> <CoinIcon coinType={coinType} - size={ImageIconSize.Small} rounded - hasCoinWrapper + size={ImageIconSize.Small} /> </CardImage> <CardBody diff --git a/apps/wallet-dashboard/components/MigrationOverview.tsx b/apps/wallet-dashboard/components/MigrationOverview.tsx new file mode 100644 index 00000000000..9b17852f3d5 --- /dev/null +++ b/apps/wallet-dashboard/components/MigrationOverview.tsx @@ -0,0 +1,39 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { useTheme, Theme } from '@iota/core'; +import { useRouter } from 'next/navigation'; +import { Banner } from './Banner'; +import { useCurrentAccount } from '@iota/dapp-kit'; +import { useGetStardustMigratableObjects } from '@/hooks'; + +export function MigrationOverview() { + const { theme } = useTheme(); + const router = useRouter(); + const account = useCurrentAccount(); + const address = account?.address || ''; + const { migratableBasicOutputs, migratableNftOutputs } = + useGetStardustMigratableObjects(address); + + const needsMigration = migratableBasicOutputs.length > 0 || migratableNftOutputs.length > 0; + + const videoSrc = + theme === Theme.Dark + ? 'https://files.iota.org/media/tooling/wallet-dashboard-migration-dark.mp4' + : 'https://files.iota.org/media/tooling/wallet-dashboard-migration-light.mp4'; + + function handleButtonClick() { + router.push('/migrations'); + } + return needsMigration ? ( + <div style={{ gridArea: 'migration' }} className="with-migration flex grow overflow-hidden"> + <Banner + videoSrc={videoSrc} + title="Migration" + subtitle="Fast & Easy" + onButtonClick={handleButtonClick} + buttonText="Start Migration" + /> + </div> + ) : null; +} diff --git a/apps/wallet-dashboard/components/index.ts b/apps/wallet-dashboard/components/index.ts index a1851aa5995..37ff834be4f 100644 --- a/apps/wallet-dashboard/components/index.ts +++ b/apps/wallet-dashboard/components/index.ts @@ -25,3 +25,5 @@ export * from './ValidatorStakingData'; export * from './tiles'; export * from './Toaster'; export * from './Banner'; +export * from './MigrationOverview'; +export * from './staked-timelock-object'; diff --git a/apps/wallet-dashboard/components/staked-timelock-object/StakedTimelockObject.tsx b/apps/wallet-dashboard/components/staked-timelock-object/StakedTimelockObject.tsx new file mode 100644 index 00000000000..8696705bc12 --- /dev/null +++ b/apps/wallet-dashboard/components/staked-timelock-object/StakedTimelockObject.tsx @@ -0,0 +1,74 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +'use client'; +import { TimelockedStakedObjectsGrouped } from '@/lib/utils'; +import { Card, CardImage, CardBody, CardAction, CardActionType } from '@iota/apps-ui-kit'; +import { useFormatCoin, ImageIcon, ImageIconSize, useStakeRewardStatus } from '@iota/core'; +import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; +import { IotaValidatorSummary } from '@iota/iota-sdk/client'; + +export interface StakedTimelockObjectProps { + timelockedStakedObject: TimelockedStakedObjectsGrouped; + handleUnstake: (timelockedStakedObject: TimelockedStakedObjectsGrouped) => void; + getValidatorByAddress: (validatorAddress: string) => IotaValidatorSummary | undefined; + currentEpoch: number; +} + +export function StakedTimelockObject({ + getValidatorByAddress, + timelockedStakedObject, + handleUnstake, + currentEpoch, +}: StakedTimelockObjectProps) { + const name = + getValidatorByAddress(timelockedStakedObject.validatorAddress)?.name || + timelockedStakedObject.validatorAddress; + + // TODO probably we could calculate estimated reward on grouping stage. + const summary = timelockedStakedObject.stakes.reduce( + (acc, stake) => { + const estimatedReward = stake.status === 'Active' ? stake.estimatedReward : 0; + + return { + principal: BigInt(stake.principal) + acc.principal, + estimatedReward: BigInt(estimatedReward) + acc.estimatedReward, + stakeRequestEpoch: stake.stakeRequestEpoch, + }; + }, + { + principal: 0n, + estimatedReward: 0n, + stakeRequestEpoch: '', + }, + ); + + const supportingText = useStakeRewardStatus({ + currentEpoch, + stakeRequestEpoch: summary.stakeRequestEpoch, + estimatedReward: summary.estimatedReward, + inactiveValidator: false, + }); + + const [sumPrincipalFormatted, sumPrincipalSymbol] = useFormatCoin( + summary.principal, + IOTA_TYPE_ARG, + ); + + return ( + <Card onClick={() => handleUnstake(timelockedStakedObject)}> + <CardImage> + <ImageIcon src={null} label={name} fallback={name} size={ImageIconSize.Large} /> + </CardImage> + <CardBody + title={name} + subtitle={`${sumPrincipalFormatted} ${sumPrincipalSymbol}`} + isTextTruncated + /> + <CardAction + type={CardActionType.SupportingText} + title={supportingText.title} + subtitle={supportingText.subtitle} + /> + </Card> + ); +} diff --git a/apps/wallet-dashboard/components/staked-timelock-object/index.ts b/apps/wallet-dashboard/components/staked-timelock-object/index.ts new file mode 100644 index 00000000000..2be2613f3ef --- /dev/null +++ b/apps/wallet-dashboard/components/staked-timelock-object/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +export * from './StakedTimelockObject'; diff --git a/apps/wallet-dashboard/hooks/index.ts b/apps/wallet-dashboard/hooks/index.ts index 373c4e399dc..77ea2304aa4 100644 --- a/apps/wallet-dashboard/hooks/index.ts +++ b/apps/wallet-dashboard/hooks/index.ts @@ -10,3 +10,4 @@ export * from './useCreateSendAssetTransaction'; export * from './useGetCurrentEpochStartTimestamp'; export * from './useTimelockedUnstakeTransaction'; export * from './useExplorerLinkGetter'; +export * from './useGetStardustMigratableObjects'; diff --git a/apps/wallet-dashboard/hooks/useGetStardustMigratableObjects.ts b/apps/wallet-dashboard/hooks/useGetStardustMigratableObjects.ts new file mode 100644 index 00000000000..7a5b40ce140 --- /dev/null +++ b/apps/wallet-dashboard/hooks/useGetStardustMigratableObjects.ts @@ -0,0 +1,47 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import { useGetCurrentEpochStartTimestamp } from '@/hooks'; +import { groupStardustObjectsByMigrationStatus } from '@/lib/utils'; +import { + STARDUST_BASIC_OUTPUT_TYPE, + STARDUST_NFT_OUTPUT_TYPE, + useGetAllOwnedObjects, +} from '@iota/core'; +import { IotaObjectData } from '@iota/iota-sdk/client'; + +export function useGetStardustMigratableObjects(address: string): { + migratableBasicOutputs: IotaObjectData[]; + unmigratableBasicOutputs: IotaObjectData[]; + migratableNftOutputs: IotaObjectData[]; + unmigratableNftOutputs: IotaObjectData[]; +} { + const { data: currentEpochMs } = useGetCurrentEpochStartTimestamp(); + const { data: basicOutputObjects } = useGetAllOwnedObjects(address, { + StructType: STARDUST_BASIC_OUTPUT_TYPE, + }); + const { data: nftOutputObjects } = useGetAllOwnedObjects(address, { + StructType: STARDUST_NFT_OUTPUT_TYPE, + }); + + const { migratable: migratableBasicOutputs, unmigratable: unmigratableBasicOutputs } = + groupStardustObjectsByMigrationStatus( + basicOutputObjects ?? [], + Number(currentEpochMs), + address, + ); + + const { migratable: migratableNftOutputs, unmigratable: unmigratableNftOutputs } = + groupStardustObjectsByMigrationStatus( + nftOutputObjects ?? [], + Number(currentEpochMs), + address, + ); + + return { + migratableBasicOutputs, + unmigratableBasicOutputs, + migratableNftOutputs, + unmigratableNftOutputs, + }; +} diff --git a/apps/wallet-dashboard/lib/interfaces/vesting.interface.ts b/apps/wallet-dashboard/lib/interfaces/vesting.interface.ts index c997986f065..ba846d71e6c 100644 --- a/apps/wallet-dashboard/lib/interfaces/vesting.interface.ts +++ b/apps/wallet-dashboard/lib/interfaces/vesting.interface.ts @@ -14,10 +14,11 @@ export interface SupplyIncreaseVestingPayout { export type SupplyIncreaseVestingPortfolio = SupplyIncreaseVestingPayout[]; export interface VestingOverview { - totalVested: number; - totalUnlocked: number; - totalLocked: number; - totalStaked: number; - availableClaiming: number; - availableStaking: number; + totalVested: bigint; + totalUnlocked: bigint; + totalLocked: bigint; + totalStaked: bigint; + totalEarned: bigint; + availableClaiming: bigint; + availableStaking: bigint; } diff --git a/apps/wallet-dashboard/lib/utils/migration.ts b/apps/wallet-dashboard/lib/utils/migration.ts index 49f54b89867..ee129d4d211 100644 --- a/apps/wallet-dashboard/lib/utils/migration.ts +++ b/apps/wallet-dashboard/lib/utils/migration.ts @@ -20,17 +20,14 @@ export function groupStardustObjectsByMigrationStatus( const epochUnix = epochTimestamp / 1000; for (const outputObject of stardustOutputObjects) { - const outputObjectFields = ( - outputObject.content as unknown as { - fields: CommonOutputObjectWithUc; - } - ).fields; + const outputObjectFields = extractOutputFields(outputObject); if (outputObjectFields.expiration_uc) { const unlockableAddress = outputObjectFields.expiration_uc.fields.unix_time <= epochUnix ? outputObjectFields.expiration_uc.fields.return_address : outputObjectFields.expiration_uc.fields.owner; + if (unlockableAddress !== address) { unmigratable.push(outputObject); continue; @@ -49,3 +46,56 @@ export function groupStardustObjectsByMigrationStatus( return { migratable, unmigratable }; } + +interface MigratableObjectsData { + totalNativeTokens: number; + totalVisualAssets: number; + accumulatedIotaAmount: number; +} + +export function summarizeMigratableObjectValues({ + migratableBasicOutputs, + migratableNftOutputs, + address, +}: { + migratableBasicOutputs: IotaObjectData[]; + migratableNftOutputs: IotaObjectData[]; + address: string; +}): MigratableObjectsData { + let totalNativeTokens = 0; + let totalIotaAmount = 0; + + const totalVisualAssets = migratableNftOutputs.length; + const outputObjects = [...migratableBasicOutputs, ...migratableNftOutputs]; + + for (const output of outputObjects) { + const outputObjectFields = extractOutputFields(output); + + totalIotaAmount += parseInt(outputObjectFields.balance); + totalNativeTokens += parseInt(outputObjectFields.native_tokens.fields.size); + totalIotaAmount += extractStorageDepositReturnAmount(outputObjectFields, address) || 0; + } + + return { totalNativeTokens, totalVisualAssets, accumulatedIotaAmount: totalIotaAmount }; +} + +function extractStorageDepositReturnAmount( + { storage_deposit_return_uc }: CommonOutputObjectWithUc, + address: string, +): number | null { + if ( + storage_deposit_return_uc?.fields && + storage_deposit_return_uc?.fields.return_address === address + ) { + return parseInt(storage_deposit_return_uc?.fields.return_amount); + } + return null; +} + +function extractOutputFields(outputObject: IotaObjectData): CommonOutputObjectWithUc { + return ( + outputObject.content as unknown as { + fields: CommonOutputObjectWithUc; + } + ).fields; +} diff --git a/apps/wallet-dashboard/lib/utils/vesting/vesting.spec.ts b/apps/wallet-dashboard/lib/utils/vesting/vesting.spec.ts index 5e1cea77657..e0bb1f37bd5 100644 --- a/apps/wallet-dashboard/lib/utils/vesting/vesting.spec.ts +++ b/apps/wallet-dashboard/lib/utils/vesting/vesting.spec.ts @@ -21,6 +21,10 @@ import { const MOCKED_CURRENT_EPOCH_TIMESTAMP = Date.now() + MILLISECONDS_PER_HOUR * 6; // 6 hours later +function bigIntRound(n: number) { + return BigInt(Math.floor(n)); +} + describe('get last supply increase vesting payout', () => { it('should get the object with highest expirationTimestampMs', () => { const timelockedObjects = MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS; @@ -121,11 +125,12 @@ describe('vesting overview', () => { it('should get correct vesting overview data with timelocked objects', () => { const timelockedObjects = MOCKED_SUPPLY_INCREASE_VESTING_TIMELOCKED_OBJECTS; const lastPayout = timelockedObjects[timelockedObjects.length - 1]; - const totalAmount = + const totalAmount = bigIntRound( (SUPPLY_INCREASE_STAKER_VESTING_DURATION * SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR * lastPayout.locked.value) / - 0.9; + 0.9, + ); const vestingOverview = getVestingOverview(timelockedObjects, Date.now()); expect(vestingOverview.totalVested).toEqual(totalAmount); @@ -140,25 +145,31 @@ describe('vesting overview', () => { const lockedAmount = vestingPortfolio.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() ? acc + current.amount : acc, - 0, + current.expirationTimestampMs > Date.now() + ? acc + bigIntRound(current.amount) + : acc, + 0n, ); expect(vestingOverview.totalLocked).toEqual(lockedAmount); expect(vestingOverview.totalUnlocked).toEqual(totalAmount - lockedAmount); // In this scenario there are no staked objects - expect(vestingOverview.totalStaked).toEqual(0); + expect(vestingOverview.totalStaked).toEqual(0n); const lockedObjectsAmount = timelockedObjects.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() ? acc + current.locked.value : acc, - 0, + current.expirationTimestampMs > Date.now() + ? acc + bigIntRound(current.locked.value) + : acc, + 0n, ); const unlockedObjectsAmount = timelockedObjects.reduce( (acc, current) => - current.expirationTimestampMs <= Date.now() ? acc + current.locked.value : acc, - 0, + current.expirationTimestampMs <= Date.now() + ? acc + bigIntRound(current.locked.value) + : acc, + 0n, ); expect(vestingOverview.availableClaiming).toEqual(unlockedObjectsAmount); @@ -172,11 +183,13 @@ describe('vesting overview', () => { const lastPayout = extendedTimelockedStakedObjects[extendedTimelockedStakedObjects.length - 1]; const lastPayoutValue = Number(lastPayout.principal); - const totalAmount = + const totalAmount = bigIntRound( (SUPPLY_INCREASE_STAKER_VESTING_DURATION * SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR * lastPayoutValue) / - 0.9; + 0.9, + ); + const vestingOverview = getVestingOverview(extendedTimelockedStakedObjects, Date.now()); expect(vestingOverview.totalVested).toEqual(totalAmount); @@ -190,18 +203,20 @@ describe('vesting overview', () => { const lockedAmount = vestingPortfolio.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() ? acc + current.amount : acc, - 0, + current.expirationTimestampMs > Date.now() + ? acc + bigIntRound(current.amount) + : acc, + 0n, ); expect(vestingOverview.totalLocked).toEqual(lockedAmount); expect(vestingOverview.totalUnlocked).toEqual(totalAmount - lockedAmount); - let totalStaked: number = 0; + let totalStaked = 0n; for (const timelockedStakedObject of timelockedStakedObjects) { const stakesAmount = timelockedStakedObject.stakes.reduce( - (acc, current) => acc + Number(current.principal), - 0, + (acc, current) => acc + bigIntRound(Number(current.principal)), + 0n, ); totalStaked += stakesAmount; } @@ -209,8 +224,8 @@ describe('vesting overview', () => { expect(vestingOverview.totalStaked).toEqual(totalStaked); // In this scenario there are no objects to stake or claim because they are all staked - expect(vestingOverview.availableClaiming).toEqual(0); - expect(vestingOverview.availableStaking).toEqual(0); + expect(vestingOverview.availableClaiming).toEqual(0n); + expect(vestingOverview.availableStaking).toEqual(0n); }); it('should get correct vesting overview data with mixed objects', () => { @@ -224,11 +239,12 @@ describe('vesting overview', () => { mixedObjects, MOCKED_CURRENT_EPOCH_TIMESTAMP, )!; - const totalAmount = + const totalAmount = bigIntRound( (SUPPLY_INCREASE_STAKER_VESTING_DURATION * SUPPLY_INCREASE_VESTING_PAYOUTS_IN_1_YEAR * lastPayout.amount) / - 0.9; + 0.9, + ); const vestingOverview = getVestingOverview(mixedObjects, Date.now()); expect(vestingOverview.totalVested).toEqual(totalAmount); @@ -243,16 +259,18 @@ describe('vesting overview', () => { const lockedAmount = vestingPortfolio.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() ? acc + current.amount : acc, - 0, + current.expirationTimestampMs > Date.now() + ? acc + bigIntRound(current.amount) + : acc, + 0n, ); expect(vestingOverview.totalLocked).toEqual(lockedAmount); expect(vestingOverview.totalUnlocked).toEqual(totalAmount - lockedAmount); const totalStaked = extendedTimelockedStakedObjects.reduce( - (acc, current) => acc + Number(current.principal), - 0, + (acc, current) => acc + bigIntRound(Number(current.principal)), + 0n, ); expect(vestingOverview.totalStaked).toEqual(totalStaked); @@ -260,13 +278,17 @@ describe('vesting overview', () => { const timelockObjects = mixedObjects.filter(isTimelockedObject); const availableClaiming = timelockObjects.reduce( (acc, current) => - current.expirationTimestampMs <= Date.now() ? acc + current.locked.value : acc, - 0, + current.expirationTimestampMs <= Date.now() + ? acc + bigIntRound(current.locked.value) + : acc, + 0n, ); const availableStaking = timelockObjects.reduce( (acc, current) => - current.expirationTimestampMs > Date.now() ? acc + current.locked.value : acc, - 0, + current.expirationTimestampMs > Date.now() + ? acc + bigIntRound(current.locked.value) + : acc, + 0n, ); expect(vestingOverview.availableClaiming).toEqual(availableClaiming); expect(vestingOverview.availableStaking).toEqual(availableStaking); diff --git a/apps/wallet-dashboard/lib/utils/vesting/vesting.ts b/apps/wallet-dashboard/lib/utils/vesting/vesting.ts index c4bd743b5ef..e0bb8a29158 100644 --- a/apps/wallet-dashboard/lib/utils/vesting/vesting.ts +++ b/apps/wallet-dashboard/lib/utils/vesting/vesting.ts @@ -149,52 +149,61 @@ export function getVestingOverview( if (vestingObjects.length === 0 || !latestPayout) { return { - totalVested: 0, - totalUnlocked: 0, - totalLocked: 0, - totalStaked: 0, - availableClaiming: 0, - availableStaking: 0, + totalVested: 0n, + totalUnlocked: 0n, + totalLocked: 0n, + totalStaked: 0n, + totalEarned: 0n, + availableClaiming: 0n, + availableStaking: 0n, }; } const userType = getSupplyIncreaseVestingUserType([latestPayout]); const vestingPayoutsCount = getSupplyIncreaseVestingPayoutsCount(userType!); // Note: we add the initial payout to the total rewards, 10% of the total rewards are paid out immediately - const totalVestedAmount = (vestingPayoutsCount * latestPayout.amount) / 0.9; + const totalVestedAmount = BigInt(Math.floor((vestingPayoutsCount * latestPayout.amount) / 0.9)); const vestingPortfolio = buildSupplyIncreaseVestingSchedule( latestPayout, currentEpochTimestamp, ); const totalLockedAmount = vestingPortfolio.reduce( (acc, current) => - current.expirationTimestampMs > currentEpochTimestamp ? acc + current.amount : acc, - 0, + current.expirationTimestampMs > currentEpochTimestamp + ? acc + BigInt(current.amount) + : acc, + 0n, ); const totalUnlockedVestedAmount = totalVestedAmount - totalLockedAmount; const timelockedStakedObjects = vestingObjects.filter(isTimelockedStakedIota); const totalStaked = timelockedStakedObjects.reduce( - (acc, current) => acc + Number(current.principal), - 0, + (acc, current) => acc + BigInt(current.principal), + 0n, ); + const totalEarned = timelockedStakedObjects + .filter((t) => t.status === 'Active') + .reduce((acc, current) => { + return acc + BigInt(current.estimatedReward); + }, 0n); + const timelockedObjects = vestingObjects.filter(isTimelockedObject); const totalAvailableClaimingAmount = timelockedObjects.reduce( (acc, current) => current.expirationTimestampMs <= currentEpochTimestamp - ? acc + current.locked.value + ? acc + BigInt(current.locked.value) : acc, - 0, + 0n, ); const totalAvailableStakingAmount = timelockedObjects.reduce( (acc, current) => current.expirationTimestampMs > currentEpochTimestamp && current.locked.value >= MIN_STAKING_THRESHOLD - ? acc + current.locked.value + ? acc + BigInt(current.locked.value) : acc, - 0, + 0n, ); return { @@ -202,6 +211,7 @@ export function getVestingOverview( totalUnlocked: totalUnlockedVestedAmount, totalLocked: totalLockedAmount, totalStaked: totalStaked, + totalEarned: totalEarned, availableClaiming: totalAvailableClaimingAmount, availableStaking: totalAvailableStakingAmount, }; diff --git a/apps/wallet/package.json b/apps/wallet/package.json index c652c639d4c..a240f6170bb 100644 --- a/apps/wallet/package.json +++ b/apps/wallet/package.json @@ -63,7 +63,7 @@ "dotenv": "^16.4.5", "eslint-webpack-plugin": "^4.0.1", "git-rev-sync": "^3.0.2", - "happy-dom": "^10.5.1", + "happy-dom": "^15.11.7", "html-webpack-plugin": "^5.5.3", "mini-css-extract-plugin": "^2.7.6", "onchange": "^7.1.0", diff --git a/apps/wallet/src/ui/app/components/receipt-card/TxnAmount.tsx b/apps/wallet/src/ui/app/components/receipt-card/TxnAmount.tsx index 2a89a992eb3..85d4ff481e0 100644 --- a/apps/wallet/src/ui/app/components/receipt-card/TxnAmount.tsx +++ b/apps/wallet/src/ui/app/components/receipt-card/TxnAmount.tsx @@ -12,7 +12,6 @@ import { CardType, ImageType, } from '@iota/apps-ui-kit'; -import { IOTA_TYPE_ARG } from '@iota/iota-sdk/utils'; interface TxnAmountProps { amount: string | number | bigint; @@ -29,13 +28,7 @@ export function TxnAmount({ amount, coinType, subtitle, approximation }: TxnAmou return Number(amount) !== 0 ? ( <Card type={CardType.Filled}> <CardImage type={ImageType.BgSolid}> - <div className="flex h-full w-full items-center justify-center rounded-full border border-shader-neutral-light-8 text-neutral-10 dark:text-neutral-92"> - <CoinIcon - coinType={coinType} - rounded - size={coinType === IOTA_TYPE_ARG ? ImageIconSize.Small : ImageIconSize.Full} - /> - </div> + <CoinIcon coinType={coinType} rounded size={ImageIconSize.Large} hasCoinWrapper /> </CardImage> <CardBody title={`${approximation ? '~' : ''}${formatAmount} ${symbol}`} diff --git a/crates/iota-cluster-test/src/config.rs b/crates/iota-cluster-test/src/config.rs index a6b3f5bb946..85814590fab 100644 --- a/crates/iota-cluster-test/src/config.rs +++ b/crates/iota-cluster-test/src/config.rs @@ -57,7 +57,7 @@ pub struct ClusterTestOpt { #[allow(dead_code)] struct ObfuscatedPgAddress<'a>(&'a Option<String>); -impl<'a> std::fmt::Display for ObfuscatedPgAddress<'a> { +impl std::fmt::Display for ObfuscatedPgAddress<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.0 { None => write!(f, "None"), diff --git a/crates/iota-common/src/sync/notify_read.rs b/crates/iota-common/src/sync/notify_read.rs index 9d54bf39dc2..bb107e018c0 100644 --- a/crates/iota-common/src/sync/notify_read.rs +++ b/crates/iota-common/src/sync/notify_read.rs @@ -150,7 +150,7 @@ pub struct Registration<'a, K: Eq + Hash + Clone, V: Clone> { registration: Option<(K, oneshot::Receiver<V>)>, } -impl<'a, K: Eq + Hash + Clone + Unpin, V: Clone + Unpin> Future for Registration<'a, K, V> { +impl<K: Eq + Hash + Clone + Unpin, V: Clone + Unpin> Future for Registration<'_, K, V> { type Output = V; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { @@ -168,7 +168,7 @@ impl<'a, K: Eq + Hash + Clone + Unpin, V: Clone + Unpin> Future for Registration } } -impl<'a, K: Eq + Hash + Clone, V: Clone> Drop for Registration<'a, K, V> { +impl<K: Eq + Hash + Clone, V: Clone> Drop for Registration<'_, K, V> { fn drop(&mut self) { if let Some((key, receiver)) = self.registration.take() { mem::drop(receiver); diff --git a/crates/iota-core/src/consensus_adapter.rs b/crates/iota-core/src/consensus_adapter.rs index 37aaa26ccd9..46e8e9b6875 100644 --- a/crates/iota-core/src/consensus_adapter.rs +++ b/crates/iota-core/src/consensus_adapter.rs @@ -963,7 +963,7 @@ impl<'a> InflightDropGuard<'a> { } } -impl<'a> Drop for InflightDropGuard<'a> { +impl Drop for InflightDropGuard<'_> { fn drop(&mut self) { self.adapter .num_inflight_transactions diff --git a/crates/iota-e2e-tests/tests/snapshot_tests.rs b/crates/iota-e2e-tests/tests/snapshot_tests.rs index 9c482884972..65cde1c5ae8 100644 --- a/crates/iota-e2e-tests/tests/snapshot_tests.rs +++ b/crates/iota-e2e-tests/tests/snapshot_tests.rs @@ -56,19 +56,17 @@ async fn basic_read_cmd_snapshot_tests() -> Result<(), anyhow::Error> { let mut test_cluster = TestClusterBuilder::new().build().await; let context = &mut test_cluster.wallet; + // These object ids and transaction digest are picked by executing this test and + // copying over some random ids from the snapshot file let cmds = vec![ "iota client objects {ME}", // valid addr "iota client objects 0x0000000000000000000000000000000000000000000000000000000000000000", /* empty addr */ "iota client object 0x5", // valid object "iota client object 0x5 --bcs", // valid object BCS - // Simtest object IDs are not stable so these object IDs may or may not exist currently -- - // commenting them out for now. - // "iota client object 0x3b5121a0603ef7ab4cb57827fceca17db3338ef2cd76126cc1523b681df27cee", - // // valid object "iota client object - // 0x3b5121a0603ef7ab4cb57827fceca17db3338ef2cd76126cc1523b681df27cee --bcs", // valid - // object BCS + "iota client object 0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", /* valid object */ + "iota client object 0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1 --bcs", /* valid object BCS */ "iota client object 0x0000000000000000000000000000000000000000000000000000000000000000", /* non-existent object */ - "iota client tx-block 5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", // valid tx digest + "iota client tx-block E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", // valid tx digest "iota client tx-block 11111111111111111111111111111111", /* non-existent tx * digest */ ]; diff --git a/crates/iota-e2e-tests/tests/snapshots/snapshot_tests__body_fn.snap b/crates/iota-e2e-tests/tests/snapshots/snapshot_tests__body_fn.snap index 90e0d1468c9..8a61ca65af0 100644 --- a/crates/iota-e2e-tests/tests/snapshots/snapshot_tests__body_fn.snap +++ b/crates/iota-e2e-tests/tests/snapshots/snapshot_tests__body_fn.snap @@ -1,21 +1,20 @@ --- source: crates/iota-e2e-tests/tests/snapshot_tests.rs expression: "run_one(cmds, context).await?" -snapshot_kind: text --- [ "iota client objects {ME}", [ { "data": { - "objectId": "0x5b0e7d31f9c3a421f94dae3511983a2be67e04a7071171da2380f82be103239f", + "objectId": "0x495af1962cebf47d1eb0cdabb02508114dc8a05c8bb4e6c18b8e81af78c8c4b5", "version": "1", - "digest": "6s1xQf5HR8HMLNaR1QxPcfV4Ys8AL94UUg7cRpD6r4WD", + "digest": "A7cf1SRMJYAqceQb78geXHujpHQuiqEggZnWu24enHu1", "type": "0x2::coin::Coin<0x2::iota::IOTA>", "owner": { "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, - "previousTransaction": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "storageRebate": "0", "content": { "dataType": "moveObject", @@ -23,7 +22,7 @@ snapshot_kind: text "fields": { "balance": "30000000000000000", "id": { - "id": "0x5b0e7d31f9c3a421f94dae3511983a2be67e04a7071171da2380f82be103239f" + "id": "0x495af1962cebf47d1eb0cdabb02508114dc8a05c8bb4e6c18b8e81af78c8c4b5" } } } @@ -31,14 +30,14 @@ snapshot_kind: text }, { "data": { - "objectId": "0x5ee269cea868091ea1c5da5f15581c2f7b1708413b3bdda3e4c9528f2b3a5bb9", + "objectId": "0x6689ff8b1b4ce68bde6f39ebd1031e0339a4036a66b7d0a85c366ac0e6817748", "version": "1", - "digest": "7mvhi6RxxMnPohJzMnCYoLHBTLUcRtwFtBXe1ZoanE6D", + "digest": "GYs13Dvcx4XAj76Q4fYfXEgFTyUvVfZYorzvFv8KBRrc", "type": "0x2::coin::Coin<0x2::iota::IOTA>", "owner": { "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, - "previousTransaction": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "storageRebate": "0", "content": { "dataType": "moveObject", @@ -46,7 +45,7 @@ snapshot_kind: text "fields": { "balance": "30000000000000000", "id": { - "id": "0x5ee269cea868091ea1c5da5f15581c2f7b1708413b3bdda3e4c9528f2b3a5bb9" + "id": "0x6689ff8b1b4ce68bde6f39ebd1031e0339a4036a66b7d0a85c366ac0e6817748" } } } @@ -54,14 +53,14 @@ snapshot_kind: text }, { "data": { - "objectId": "0x759bb64d0a576b00a8aae6d439da4a72ef7d8797282c90211bab2bc723cd48e5", + "objectId": "0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", "version": "1", - "digest": "7cuKDvum6Rx2utLqquqYZGr6K3S5xbA2fq88cqs8qjZd", + "digest": "5B8YV4P7Q93phJVPgm8MVZzsoXdSxFnKLAjAkMnLYcQb", "type": "0x2::coin::Coin<0x2::iota::IOTA>", "owner": { "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, - "previousTransaction": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "storageRebate": "0", "content": { "dataType": "moveObject", @@ -69,7 +68,7 @@ snapshot_kind: text "fields": { "balance": "30000000000000000", "id": { - "id": "0x759bb64d0a576b00a8aae6d439da4a72ef7d8797282c90211bab2bc723cd48e5" + "id": "0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1" } } } @@ -77,14 +76,14 @@ snapshot_kind: text }, { "data": { - "objectId": "0xa010e1486bfd2f1d7c57a3b2c2edd369e64e8fddc1ca17667314317273cb491d", + "objectId": "0x94b18ab08b2ddd60e7db0756a605bd31589136eb861b76f49ff3b5ae688f65bc", "version": "1", - "digest": "DAFo7bkSkk2XRZv88Nb2V9jatTse1TYTqgMmVxVKGRtF", + "digest": "DXGesd5mWPPaLtwza28GDfumr9NqswcfpbcHp2LaL287", "type": "0x2::coin::Coin<0x2::iota::IOTA>", "owner": { "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, - "previousTransaction": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "storageRebate": "0", "content": { "dataType": "moveObject", @@ -92,7 +91,7 @@ snapshot_kind: text "fields": { "balance": "30000000000000000", "id": { - "id": "0xa010e1486bfd2f1d7c57a3b2c2edd369e64e8fddc1ca17667314317273cb491d" + "id": "0x94b18ab08b2ddd60e7db0756a605bd31589136eb861b76f49ff3b5ae688f65bc" } } } @@ -100,14 +99,14 @@ snapshot_kind: text }, { "data": { - "objectId": "0xb279227ad10111e5d870facc26d048a05bc440859e718cde0e3bbf257765544c", + "objectId": "0x9ee19bb4564baa0449b5160e4dccd02d5d8a54ca794aaba2130db4feb809c1f5", "version": "1", - "digest": "BZaGDacxqx5MFLFm6F3UeBVpHG5hZykqSHFoD1o5F5kq", + "digest": "zoUU8mDtdVPRCqfRKWmd5VpMNfgBHd4jVjq4gu8i9vn", "type": "0x2::coin::Coin<0x2::iota::IOTA>", "owner": { "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, - "previousTransaction": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "storageRebate": "0", "content": { "dataType": "moveObject", @@ -115,7 +114,7 @@ snapshot_kind: text "fields": { "balance": "30000000000000000", "id": { - "id": "0xb279227ad10111e5d870facc26d048a05bc440859e718cde0e3bbf257765544c" + "id": "0x9ee19bb4564baa0449b5160e4dccd02d5d8a54ca794aaba2130db4feb809c1f5" } } } @@ -130,14 +129,14 @@ snapshot_kind: text "data": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000005", "version": "1", - "digest": "73woKiMAUkS1ejdzSwyD1WLDCxsPVYURbwpMBa64Tu4h", + "digest": "8CWv4f5Nov5aRXTQa2EJk7zsNhfKVbX1fEG8VP6Wsa6d", "type": "0x3::iota_system::IotaSystemState", "owner": { "Shared": { "initial_shared_version": 1 } }, - "previousTransaction": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "storageRebate": "0", "content": { "dataType": "moveObject", @@ -158,14 +157,14 @@ snapshot_kind: text "data": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000005", "version": "1", - "digest": "73woKiMAUkS1ejdzSwyD1WLDCxsPVYURbwpMBa64Tu4h", + "digest": "8CWv4f5Nov5aRXTQa2EJk7zsNhfKVbX1fEG8VP6Wsa6d", "type": "0x3::iota_system::IotaSystemState", "owner": { "Shared": { "initial_shared_version": 1 } }, - "previousTransaction": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "storageRebate": "0", "bcs": { "dataType": "moveObject", @@ -176,6 +175,54 @@ snapshot_kind: text } } ], + "iota client object 0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", + [ + { + "data": { + "objectId": "0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", + "version": "1", + "digest": "5B8YV4P7Q93phJVPgm8MVZzsoXdSxFnKLAjAkMnLYcQb", + "type": "0x2::coin::Coin<0x2::iota::IOTA>", + "owner": { + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + }, + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", + "storageRebate": "0", + "content": { + "dataType": "moveObject", + "type": "0x2::coin::Coin<0x2::iota::IOTA>", + "fields": { + "balance": "30000000000000000", + "id": { + "id": "0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1" + } + } + } + } + } + ], + "iota client object 0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1 --bcs", + [ + { + "data": { + "objectId": "0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", + "version": "1", + "digest": "5B8YV4P7Q93phJVPgm8MVZzsoXdSxFnKLAjAkMnLYcQb", + "type": "0x2::coin::Coin<0x2::iota::IOTA>", + "owner": { + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + }, + "previousTransaction": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", + "storageRebate": "0", + "bcs": { + "dataType": "moveObject", + "type": "0x2::coin::Coin<0x2::iota::IOTA>", + "version": 1, + "bcsBytes": "kTXLO1rKmaFVW3Qr0R3cRfujM0O+GCvcFhvmnaLEG+EAAENP15RqAA==" + } + } + } + ], "iota client object 0x0000000000000000000000000000000000000000000000000000000000000000", [ { @@ -185,9 +232,9 @@ snapshot_kind: text } } ], - "iota client tx-block 5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "iota client tx-block E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", { - "digest": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "digest": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "transaction": { "data": { "messageVersion": "v1", @@ -203,64 +250,64 @@ snapshot_kind: text "0x000000000000000000000000000000000000000000000000000000000000000b", "0x0000000000000000000000000000000000000000000000000000000000000403", "0x000000000000000000000000000000000000000000000000000000000000107a", - "0x018673f8b9c2eee4dd923fb6fcb3eb77d0e33d15921ac59f1c57ea7dfe4c3f40", - "0x08e1bd72a9f239c8e99a3ee8f1f4141b48ba063ecc0ebff878152e2b481b94bc", - "0x163b3f079eb959dc96768c65497ef5c3b48106439a306324c484a8d28e0ba50f", - "0x16d23fb8f64faebeea28ecdd43d96cb60ed1c5a297347d61a0c3fa4fff08944e", - "0x1a1f82376293f736136384ae03744241eb257a82f25eb223c7ef02e67442d6df", - "0x1d8f7a031fcca49d212b5bf2b57704913f7c6ca423357a01dda70a150e8a35b0", - "0x1f3119f496e7c38af65bc0f29ee79354de5f554d03d0aa433bee015e280e97b4", - "0x2438ca49fb81fc37f74d890207c3a922b27de14b474525b4fa111de094e5194c", - "0x303fac6452e9b844d960dcd60c84efbbd28b017774787c58705b67af946d7076", - "0x3238c6a409ed6951125aaea3334cf632e053e0d09507e8a53d4b4b5923b896d0", - "0x3873a7b96acdc98bfa6db49a096f8e704caa6dc7cca29aecff52abe11442dc3f", - "0x3e5b52bc91e54ac367f5987785c7b0a68869a4be2eb0a74f7bee2172e0a70dbc", - "0x3fb0323b083da0415032b289cb6e610388de91f9666a67c02df8ddd9edb00f4c", - "0x4451b2f6d468c331dafa10f4b3fbf057b89f684553bf9298d0c4f8e61df2fa95", - "0x4547fa20f95dcc6e221d18eaf933f993ee8f46f98eef1705853a71f9c917c185", - "0x49315560aecdcf0beba3cfccb2d04f29f5d95b440cb0b6dadec64349b81083d6", - "0x497d698dce0d808d05cb3543b2decd461eb67a0a832acf7e3d822ff61663acb4", - "0x4a4a868c023e670163f57e2467cccdaedef147daf5170fc9b0ce7caa48284a76", - "0x4df66b7aab12f518bfba67b0081cadb8a321870b171a98d2aceafb6804e3b003", - "0x50948c5a6a706b267a6b2c2a4a2043d28ba11c5e022198f58be4182d65987a2d", - "0x58efd4d54b74b390e2d3bbe785ec5df555c366b1a6eb616fb07c44d6c3f67c8c", - "0x5b0e7d31f9c3a421f94dae3511983a2be67e04a7071171da2380f82be103239f", - "0x5c995a223cf5e88e2d19935481d2d06b2e55977cb3615ad6784cc360a42d6501", - "0x5ee269cea868091ea1c5da5f15581c2f7b1708413b3bdda3e4c9528f2b3a5bb9", - "0x61f2f500cb861d8d92affc84519c4c0a6b794058b58607768c8e38ab275de0eb", + "0x00d74df673e00106b1a45136cf8cfae1ca792c3de53232e06d09fbf5d4f60b66", + "0x0214b29e560e3fa6b37836ed7167db0da40a1823cf8b01f0690e1b3bfb4ebdc5", + "0x04942308c8384041a5af2c13c807418d069d3a6e53519dadd1236f0344385a14", + "0x137cb52b1bbef8e93857fd3dc67f7f70f2aabc1d7d6eb4afb28e7dea87b9ccf1", + "0x2a74fa6e7b6f90225cb36fcdc3e416d1d3d9e209ab14aafff3afd2c3587c9aec", + "0x2ad996aa647ab628cf294b4d98b24c9debb351e72b4a72ee3425b7d9a41d08a8", + "0x30f537424026a4259959d4c28f4b474558ba881bb517267f9dac6e7bc7518a5d", + "0x3ad5e2039f28a1d146d5f7e2094b7e55f1cc35f34a53aa207250d51e76febbb4", + "0x3e14d7d3f210d79f29a18955f4e98676ebb40e51119ddd6d26a8add99daceb95", + "0x3e5742903bb62d61adea88aa26bad2fad57984e3eefc58a2f1810b82228e5117", + "0x42cfd74b065c48da45fe6048796ce9cdd3a380c182e7d89573fabc229e3ce4ab", + "0x4728ee263c6a7de9fcd0d4d58db500e0ceed99977f44ca323e9a521970efd085", + "0x495af1962cebf47d1eb0cdabb02508114dc8a05c8bb4e6c18b8e81af78c8c4b5", + "0x608863c1d4738cb9829f9bd7c5b3ab15123b6e6eab762dedcfacc23ec23d37c9", + "0x660e677bad9d848d1f131d14faef42025eedf0f0e14ede894760e0ebb51a80da", + "0x6689ff8b1b4ce68bde6f39ebd1031e0339a4036a66b7d0a85c366ac0e6817748", + "0x69cd03a3cc6b2e4a1eae6bef4892f642d8f87ed3d83b758cc5242034bd36f565", "0x6af2a2b7ca60bf76174adfd3e9c4957f8e937759603182f9b46c7f6c5f19c6d2", - "0x6b517df3c5888ce924d7871e550989f835335c726a43a224ce3df6b6dfa53666", - "0x759bb64d0a576b00a8aae6d439da4a72ef7d8797282c90211bab2bc723cd48e5", - "0x76472cb3b720491fb9a66db21befaed5fbc8528976bc6fb2bc3c5deeef9d114c", - "0x7c9f819b470ae3ac98b6e44eeea9b461d66a38b22a3d2f61ce62c69f68d0c71a", - "0x81c7cfd9c7fa81f57a59f14739ee82157fc6a04d48a1b0a6e1187a8898042299", - "0x85a7aabfb1d8ef14efd87338a58efcdbfaf0cab5e5fc247ccd1080dcdd3d2d7f", - "0x8cd9aca3790437fc6b1b7258c5fefb2e95ffb54134aae1fd04ba757d4286055d", - "0x8fd46357d5ae46f9ee8de7d9b6dfc9da4e497c70dbd2ce5c2fc124658798d5c9", - "0xa010e1486bfd2f1d7c57a3b2c2edd369e64e8fddc1ca17667314317273cb491d", - "0xa6e659b1120b373f598d1f6c3214d73abda596eb913857e1fa471ab4420f2e27", - "0xb271c22281276193f817eeab5291b79f5f37698a21323fbafe014e1774c3d22e", - "0xb279227ad10111e5d870facc26d048a05bc440859e718cde0e3bbf257765544c", - "0xbb43894417b1b4a4cee286133d85cae9706c217d150daf4d12168bc5e9147d78", - "0xc066a3793934a82847a3cee2a0dd84832ba1ba24b71ac0602b1a60cbbe553d17", - "0xc79de55a6454ac8e927645a08e4e9b3ce8e814ce9fc25eb7a347a91fc2228f4f", - "0xc7ee80d93a34f12ff58b0ceaf16c4e06ee6ceeb547b031b0d3eab1a8a6ca873f", - "0xd1e455eb2c367047a03756eb830bf0d2357b0c69905f0d4f3a648cc81498905e", - "0xd26171201bf3e074f1d1c694ea6ebc5d95d75b75583c393055efbca3eeb9c629", - "0xd6090aa14b28b0e907434cd8f8469b24769a80ebd722f18df48fe59e5fd47c0f", - "0xe47935c2607151b36e2e7d6971418157d7a8997ab2771ae779f5b6a2230334c9", - "0xe54599f1bb4b687498caf1e5212c6eaf0b088dcc8159d283ecf2c6287ede48dc", - "0xefd24c24d3784edec255308f6fc3619f65902ebd122f5a34b105727994326ce1", - "0xfbf79fe230691c666820739add167373a61a7faa858231f42feb961ec8fffcf5", - "0xffc04eb3011a2933dd0fbfff34cbd6759dbf999f7e2ac372e08d577c56a459cb" + "0x6b2a75a39565fe93f6f2694e79274205ec65dac6aecff0a38b3c3947e2c9e561", + "0x82054bc6567488b4924203907f0bc62f3f789e1f5edc901798c5a58042dfa1a8", + "0x8a23c86abe72190400e37fd3affc876000ad1302712cbdaf76077f7d541b6400", + "0x9045719b475148ea5e0547b2a0dd8f8aaa19a658deac6bfca7a184d1dfa13d87", + "0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", + "0x9254a40663ad7e72d73dd1f04b1eca4e0486cc9f54cdd9a7c3d4e479a8131aa0", + "0x939337494e5c4fe00e78f120459bf108367938719f717e32c845ab83e762f22c", + "0x94b18ab08b2ddd60e7db0756a605bd31589136eb861b76f49ff3b5ae688f65bc", + "0x98c27dcdc3cb082f2ae47756648c92ee054721273663cf0ad25d7f2d4fd20128", + "0x9bafea6cfb0a89e0eee6e66ec0d598848067196ed07857012e00e609e8f3ca17", + "0x9df5ecf6c7c2ed3f31b6ff471aaea04af7d331647c34ab319c8eef45dd043620", + "0x9ee19bb4564baa0449b5160e4dccd02d5d8a54ca794aaba2130db4feb809c1f5", + "0xa01451ee51efc5ef58d07e5d373b68187ee3a042553670da8a4dc613adf3ed61", + "0xa181feb7b518f4e06794c678eace3633c6b1d7222736e9d059e75ca3023b95a0", + "0xa629b978cb416fdeac008d532329429114c70e49909d97bede0f794b376eafe8", + "0xaca6bf6562b4cbbd909e6f9310c23c9517deca84e82d22dfa781c3dd980bbc95", + "0xb12dd88f42ce5db0ba12181deae915d795de5a1e4dfe87deb44a51c8dd8ced7a", + "0xc3647488b7f826d5e2602102964e93859bab9374389c1cca8799f91c2b8b1589", + "0xcc36edb95e82c14b7175ae99e58c3a27b43fb1ba7d7f1454fbda1ce66e0e5a8f", + "0xcf326c0a4f56e81beb81e1ee93cda5bd5bc5e88fa4b730a1909679b2ffb3f0d3", + "0xd3260d5e56e16920f270b3f92a40782447e360ea000aba2af08f0d1f8ad6b5f0", + "0xd43afda73e2bb89e67d8049618176ba09456438f9fcea5f8a3e91f8a1b17ecd8", + "0xd8fa8491b91a220af49eb0b54e1d5de725ca40cad8ecca989f27a2f713402490", + "0xdde2baa8f84257d47c8051b5abd214afb850211d7ee74a99c21698e8bd66f81d", + "0xe0ebce954f5f7a4f32e3fa49fa1b76bc1e56008535860b3fe78387db50f95dbd", + "0xe360ccacf592e9a2d80c7314d882d2adb850db51fd18572a6baf11a85b233e5d", + "0xe5395355afde6c5624ce706e91738cb766a8324c86ea933f38c3f8efbba38507", + "0xe8bcfc0181e426b9dfcad3785fb4db1a0d0a2b1fa40c99a36b5fe988d2a2f561", + "0xed9ffc59b7e841e0de23ac1b72c4f04a784e2fef9c46f93eed0afdd14f8d047c", + "0xeed5eebe436d3b4dedb5d25eff6275c5212fb408ab6e2d1756fd9c56b2af892d", + "0xf69faa3aa3015380b78c11aae2ee8eed497bb5ee2555e686b53e375ab90fe8cd", + "0xfe1d0d9ef3118023510cdf338ca59ae1d3e88e1ca98a3e1904d61a8620a1ad98" ], "events": [ { - "txDigest": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "txDigest": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "eventSeq": "0" }, { - "txDigest": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "txDigest": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "eventSeq": "1" } ] @@ -296,14 +343,14 @@ snapshot_kind: text "storageRebate": "0", "nonRefundableStorageFee": "0" }, - "transactionDigest": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "transactionDigest": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "created": [ { "owner": "Immutable", "reference": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000001", "version": 1, - "digest": "4JE3kcoZSKseaSsSdgQveyB6GHU2GF3oBpg5pJ39ZSFG" + "digest": "7XoN6TGQbADiyEaVmrD6gmXJDJkX21MxGMc4pYCo9mKN" } }, { @@ -311,7 +358,7 @@ snapshot_kind: text "reference": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000002", "version": 1, - "digest": "8zNipc5CUAJYs6ak77VrFXPxQyY5vwtkUmyZz8w2j5Fq" + "digest": "5DVdPqNcu7yg8VSoQy9GwmoUprvHyK87jxnbRoac1r1S" } }, { @@ -319,7 +366,7 @@ snapshot_kind: text "reference": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000003", "version": 1, - "digest": "GCLhS6D6SeU7JstsXGPEF2PTY9rGQXNLo3A6j162jdFi" + "digest": "B7r86UhqxCjrBYVNGnSf7QBNX7EWWm7kLHZ5ht1MLAre" } }, { @@ -331,7 +378,7 @@ snapshot_kind: text "reference": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000005", "version": 1, - "digest": "73woKiMAUkS1ejdzSwyD1WLDCxsPVYURbwpMBa64Tu4h" + "digest": "8CWv4f5Nov5aRXTQa2EJk7zsNhfKVbX1fEG8VP6Wsa6d" } }, { @@ -343,7 +390,7 @@ snapshot_kind: text "reference": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000006", "version": 1, - "digest": "2iNGKB1PQafgW1o6Y3ZSkUwhxJrNbbEK4PfrqW4zxokW" + "digest": "DjXTupBThwgJcFY9YDL2Xan9bwTTA3BYrW72Dgh1AMJA" } }, { @@ -355,7 +402,7 @@ snapshot_kind: text "reference": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000008", "version": 1, - "digest": "4ZVAMTUCrpGjd97gLUnqTSvk2gqJvr7FnGopRvQkVnzn" + "digest": "aCZvLjShGf5G1AEq3jb9c3y3jxi8NhX17kFUxKwt1q1" } }, { @@ -363,7 +410,7 @@ snapshot_kind: text "reference": { "objectId": "0x000000000000000000000000000000000000000000000000000000000000000b", "version": 1, - "digest": "81neBedYgjyePBFEUdm3PPC3K6BLGjwuyyFXxaDFKbAT" + "digest": "7rF3B3o4ZHxju9pxz2eGmYhigdg5MBx4WLkyYcBaMMs9" } }, { @@ -375,7 +422,7 @@ snapshot_kind: text "reference": { "objectId": "0x0000000000000000000000000000000000000000000000000000000000000403", "version": 1, - "digest": "E7zwAronHAhg8gRcCNJrzffEcMCqyGwTRvXzk6tZYTzG" + "digest": "57nJzNa4jJBPDpbVVKcntb8rEZLhwhAsLAH7o6r6YLAb" } }, { @@ -383,55 +430,57 @@ snapshot_kind: text "reference": { "objectId": "0x000000000000000000000000000000000000000000000000000000000000107a", "version": 1, - "digest": "BvAgaA1pE6cMYg7tNZv7sdafwnYCgxk9DPseprtGCXw5" + "digest": "5Ad5TaF7cAZwf8U3rTD19oxAEfrhptBrSg9qGfYf36nY" } }, { "owner": { - "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" + "AddressOwner": "0x4b2a0b010344ffda7202ecd5f76b742b78516cfcdb208e3314d65a4157654c4b" }, "reference": { - "objectId": "0x018673f8b9c2eee4dd923fb6fcb3eb77d0e33d15921ac59f1c57ea7dfe4c3f40", + "objectId": "0x00d74df673e00106b1a45136cf8cfae1ca792c3de53232e06d09fbf5d4f60b66", "version": 1, - "digest": "AkRRb1Y6sXYHYunTUhbhqZoeTF2KdJ9E2YJh8zncRof3" + "digest": "57LAitmwQMZgioGaRVybfVmZ2Yc57HrJ2Hkm7iGmX5aD" } }, { "owner": { - "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + "ObjectOwner": "0x1d3c44ed95179381aec577194b10aaaccddc14ceedf533b9b6b4eef5535476e0" }, "reference": { - "objectId": "0x08e1bd72a9f239c8e99a3ee8f1f4141b48ba063ecc0ebff878152e2b481b94bc", + "objectId": "0x0214b29e560e3fa6b37836ed7167db0da40a1823cf8b01f0690e1b3bfb4ebdc5", "version": 1, - "digest": "FszgvFokRH5rBBNoLrTxKWBGnCAdiRqXpWYixmwBvwpf" + "digest": "3w6HwgnTmaGDCnAciokuEpZHj2NjFWv6i9KiCHqH7Nct" } }, { "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" }, "reference": { - "objectId": "0x163b3f079eb959dc96768c65497ef5c3b48106439a306324c484a8d28e0ba50f", + "objectId": "0x04942308c8384041a5af2c13c807418d069d3a6e53519dadd1236f0344385a14", "version": 1, - "digest": "2ueUhUo7CYjPNknnKCgASKC7u1ifGVYa9N2GpqNEFzoE" + "digest": "811mkHeY9LoDN16a3CSnr22AZK8R2rJa9DbDwuvDp9kd" } }, { "owner": { - "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" + "ObjectOwner": "0xee64ad330164aaa798846df2f2df3525410779fb120765a1c5fb1f931bed784b" }, "reference": { - "objectId": "0x16d23fb8f64faebeea28ecdd43d96cb60ed1c5a297347d61a0c3fa4fff08944e", + "objectId": "0x137cb52b1bbef8e93857fd3dc67f7f70f2aabc1d7d6eb4afb28e7dea87b9ccf1", "version": 1, - "digest": "ApwvoSkL1WC5UcJWSbzGuNJHn9F5ZPuALucnMNP63sCF" + "digest": "9fG99ZbX8SPLLkLnRcah82PyLY3BSbgbLD7HKD6XntZ4" } }, { - "owner": "Immutable", + "owner": { + "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + }, "reference": { - "objectId": "0x1a1f82376293f736136384ae03744241eb257a82f25eb223c7ef02e67442d6df", + "objectId": "0x2a74fa6e7b6f90225cb36fcdc3e416d1d3d9e209ab14aafff3afd2c3587c9aec", "version": 1, - "digest": "CJY1zNjevKH6Kya6aWr3Ri1HQsgeZFCf9qR4HqH72Fnt" + "digest": "E37WvCF8d2asqAJH7MtR9rnyiWeHqjgsQqL4gbjAwgLk" } }, { @@ -439,349 +488,347 @@ snapshot_kind: text "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, "reference": { - "objectId": "0x1d8f7a031fcca49d212b5bf2b57704913f7c6ca423357a01dda70a150e8a35b0", + "objectId": "0x2ad996aa647ab628cf294b4d98b24c9debb351e72b4a72ee3425b7d9a41d08a8", "version": 1, - "digest": "7yn1SCAHPQVAmJANbtPKjkZyzpN675SHdNBNwdFfXGbZ" + "digest": "F5fPj6gyhcWnNMJTnxis1goTvTNUXYr69zJudZaqMt2t" } }, { - "owner": { - "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" - }, + "owner": "Immutable", "reference": { - "objectId": "0x1f3119f496e7c38af65bc0f29ee79354de5f554d03d0aa433bee015e280e97b4", + "objectId": "0x30f537424026a4259959d4c28f4b474558ba881bb517267f9dac6e7bc7518a5d", "version": 1, - "digest": "59iXLqERWshXAXJqsVuwRRmoxW4AXLv5jnH3cPxRwAXw" + "digest": "CF2vkHV4X8YVrhgVKRafUDX7HmSbxSkvmwFYFPkEE4PM" } }, { "owner": { - "AddressOwner": "0x4b2a0b010344ffda7202ecd5f76b742b78516cfcdb208e3314d65a4157654c4b" + "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" }, "reference": { - "objectId": "0x2438ca49fb81fc37f74d890207c3a922b27de14b474525b4fa111de094e5194c", + "objectId": "0x3ad5e2039f28a1d146d5f7e2094b7e55f1cc35f34a53aa207250d51e76febbb4", "version": 1, - "digest": "A5e18Gy41Bxq2McoH7FbiYJFfU1yx26XRgvDk31KbRTz" + "digest": "2B9M7nEmFp1jcPM72pd7qXNNenQZwdp5dGu4xoykGeXZ" } }, { "owner": { - "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" + "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" }, "reference": { - "objectId": "0x303fac6452e9b844d960dcd60c84efbbd28b017774787c58705b67af946d7076", + "objectId": "0x3e14d7d3f210d79f29a18955f4e98676ebb40e51119ddd6d26a8add99daceb95", "version": 1, - "digest": "D582bFMq1ftKpRhrcFeRLhEJUbfrRZZLA6Ymgs87S6cC" + "digest": "A6ZSdzLj5Bvv27zaQoFm5rUkS91sK8nJKfntqKbHepUb" } }, { "owner": { - "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" + "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, "reference": { - "objectId": "0x3238c6a409ed6951125aaea3334cf632e053e0d09507e8a53d4b4b5923b896d0", + "objectId": "0x3e5742903bb62d61adea88aa26bad2fad57984e3eefc58a2f1810b82228e5117", "version": 1, - "digest": "GZiR6Qj8jF3vb8Eq3AF3zrCrqGcHpKPFv3MYRCn4RLsT" + "digest": "3MVJSV5RKkXLxsTdHqPQERjnmAQAoxkkiRv1Q6G8zHaU" } }, { "owner": { - "ObjectOwner": "0xce3d02f2a570935f50158318f1f0f8706fb80873afce7bb914625c1082225399" + "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, "reference": { - "objectId": "0x3873a7b96acdc98bfa6db49a096f8e704caa6dc7cca29aecff52abe11442dc3f", + "objectId": "0x42cfd74b065c48da45fe6048796ce9cdd3a380c182e7d89573fabc229e3ce4ab", "version": 1, - "digest": "DmLPuMUMQHtu4hRNpXbcjqtRKWrJqPLUDnZw84GmgaUZ" + "digest": "5AzKkrWAReMDgNF2bbiveSDDWvJpQzSX7xHRHNJu9Phy" } }, { "owner": { - "ObjectOwner": "0xce3d02f2a570935f50158318f1f0f8706fb80873afce7bb914625c1082225399" + "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "reference": { - "objectId": "0x3e5b52bc91e54ac367f5987785c7b0a68869a4be2eb0a74f7bee2172e0a70dbc", + "objectId": "0x4728ee263c6a7de9fcd0d4d58db500e0ceed99977f44ca323e9a521970efd085", "version": 1, - "digest": "ARd1kECfyCcc3TUTnFZrdYRAGX6xn9G3ZKNtQKNDEA8R" + "digest": "781mLWh1jZYNUxtypVJ4XWvHvvga91AZyRfrtXSG53RZ" } }, { "owner": { - "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, "reference": { - "objectId": "0x3fb0323b083da0415032b289cb6e610388de91f9666a67c02df8ddd9edb00f4c", + "objectId": "0x495af1962cebf47d1eb0cdabb02508114dc8a05c8bb4e6c18b8e81af78c8c4b5", "version": 1, - "digest": "EidyXR5ajBRafVgkEA1cm598AdyK66F2NBub16VyE9aB" + "digest": "A7cf1SRMJYAqceQb78geXHujpHQuiqEggZnWu24enHu1" } }, { "owner": { - "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" + "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" }, "reference": { - "objectId": "0x4451b2f6d468c331dafa10f4b3fbf057b89f684553bf9298d0c4f8e61df2fa95", + "objectId": "0x608863c1d4738cb9829f9bd7c5b3ab15123b6e6eab762dedcfacc23ec23d37c9", "version": 1, - "digest": "33vUxz6x2MsvDWTQWpFpjWQbPbbZhUxPyn3uwSDfAVQx" + "digest": "6NnLvzoYdm9qBrrsgCj3x2vtrQeMceUt1QuBSvxvgXq5" } }, { "owner": { - "ObjectOwner": "0xce3d02f2a570935f50158318f1f0f8706fb80873afce7bb914625c1082225399" + "ObjectOwner": "0xaca630a6c6d4a9e7a7b033174b3952dfd7c2edeea9fa3a356e97d1a5bc31dae0" }, "reference": { - "objectId": "0x4547fa20f95dcc6e221d18eaf933f993ee8f46f98eef1705853a71f9c917c185", + "objectId": "0x660e677bad9d848d1f131d14faef42025eedf0f0e14ede894760e0ebb51a80da", "version": 1, - "digest": "J3kRd8hXaaV2mFKES9k7X2NnjxDCGcFAUoXCqsY2reZJ" + "digest": "EJiD9QzGf9afkBrdYyCfsg9f7Cx64oSLDTVMuvxc2jb2" } }, { "owner": { - "ObjectOwner": "0xbc4cf4a6e17ba874f668896d13e8dda92bab7a8870b3118d26fd67501028c2f3" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, "reference": { - "objectId": "0x49315560aecdcf0beba3cfccb2d04f29f5d95b440cb0b6dadec64349b81083d6", + "objectId": "0x6689ff8b1b4ce68bde6f39ebd1031e0339a4036a66b7d0a85c366ac0e6817748", "version": 1, - "digest": "BRyi5NozgWp2Aji5hM8p83M8VgyR3PYg7G27MiYR2B9z" + "digest": "GYs13Dvcx4XAj76Q4fYfXEgFTyUvVfZYorzvFv8KBRrc" } }, { "owner": { - "ObjectOwner": "0x672bba3ef273000a1627a5f128022e261e36a85db6c508b917aa3139a2e9720d" + "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, "reference": { - "objectId": "0x497d698dce0d808d05cb3543b2decd461eb67a0a832acf7e3d822ff61663acb4", + "objectId": "0x69cd03a3cc6b2e4a1eae6bef4892f642d8f87ed3d83b758cc5242034bd36f565", "version": 1, - "digest": "242fQBuczzCLdXuMZ7FZwR9m86DacJxUTC5xuUW62DSG" + "digest": "FSfz1Dtc26vvg9ASRGoVHGg1WVbVqLcBtvrcYtTt6bLe" } }, { "owner": { - "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" + "ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000005" }, "reference": { - "objectId": "0x4a4a868c023e670163f57e2467cccdaedef147daf5170fc9b0ce7caa48284a76", + "objectId": "0x6af2a2b7ca60bf76174adfd3e9c4957f8e937759603182f9b46c7f6c5f19c6d2", "version": 1, - "digest": "AWztWHzVjwd5rYXr7ork1gYC54JqtGpwHNdFA9i6gA16" + "digest": "Er8zLC9VrCrgZZcnhjLkA73mZ3J63rxiboYV6EsqmhN2" } }, { "owner": { - "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" + "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" }, "reference": { - "objectId": "0x4df66b7aab12f518bfba67b0081cadb8a321870b171a98d2aceafb6804e3b003", + "objectId": "0x6b2a75a39565fe93f6f2694e79274205ec65dac6aecff0a38b3c3947e2c9e561", "version": 1, - "digest": "EPxkpuZZYcjDJq9AzNLMPM8vsY1URjow7nmDuxQQ1QCb" + "digest": "Hy3XSG83B625ywGh161TYQ2VqBJzKxFccgp1fL3Zjbob" } }, { "owner": { - "ObjectOwner": "0xce3d02f2a570935f50158318f1f0f8706fb80873afce7bb914625c1082225399" + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" }, "reference": { - "objectId": "0x50948c5a6a706b267a6b2c2a4a2043d28ba11c5e022198f58be4182d65987a2d", + "objectId": "0x82054bc6567488b4924203907f0bc62f3f789e1f5edc901798c5a58042dfa1a8", "version": 1, - "digest": "9SGZtrm6U2rsCqiaFoVQEUSreYstnrCRg6f2EzwKGHq" + "digest": "DRYTDtLfuoxayHMXVqxuuWvv4DJnjgZQvoZebdCoGszX" } }, { "owner": { - "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" + "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "reference": { - "objectId": "0x58efd4d54b74b390e2d3bbe785ec5df555c366b1a6eb616fb07c44d6c3f67c8c", + "objectId": "0x8a23c86abe72190400e37fd3affc876000ad1302712cbdaf76077f7d541b6400", "version": 1, - "digest": "FbrF4mnrw5oXKX1trYT2NoENq7CaSGVZfXj5XitjSqhH" + "digest": "2fqjooDKXPoqAFBjzh6cGr53zuTfngNLuHzJoPoR7uvk" } }, { "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" }, "reference": { - "objectId": "0x5b0e7d31f9c3a421f94dae3511983a2be67e04a7071171da2380f82be103239f", + "objectId": "0x9045719b475148ea5e0547b2a0dd8f8aaa19a658deac6bfca7a184d1dfa13d87", "version": 1, - "digest": "6s1xQf5HR8HMLNaR1QxPcfV4Ys8AL94UUg7cRpD6r4WD" + "digest": "9puPcXBGsDq9hTfkFMUHpmruAkuqsqQmBt6b2ChxRU2f" } }, { "owner": { - "ObjectOwner": "0x1dd0079cb68a9c0dc5cbbb0c9c5d831bb56ed88c4a12c647f1352832092d11dc" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, "reference": { - "objectId": "0x5c995a223cf5e88e2d19935481d2d06b2e55977cb3615ad6784cc360a42d6501", + "objectId": "0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", "version": 1, - "digest": "2ih9uvSzKBSv9YhrNZdMokhPcZgZ8gTt48TyUToB6JgX" + "digest": "5B8YV4P7Q93phJVPgm8MVZzsoXdSxFnKLAjAkMnLYcQb" } }, { "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "ObjectOwner": "0xaca630a6c6d4a9e7a7b033174b3952dfd7c2edeea9fa3a356e97d1a5bc31dae0" }, "reference": { - "objectId": "0x5ee269cea868091ea1c5da5f15581c2f7b1708413b3bdda3e4c9528f2b3a5bb9", + "objectId": "0x9254a40663ad7e72d73dd1f04b1eca4e0486cc9f54cdd9a7c3d4e479a8131aa0", "version": 1, - "digest": "7mvhi6RxxMnPohJzMnCYoLHBTLUcRtwFtBXe1ZoanE6D" + "digest": "Du8gWCJQvqcsNBEpSYTDSB2aaRyVEG8h7niCPhKQtZFf" } }, { "owner": { - "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" + "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, "reference": { - "objectId": "0x61f2f500cb861d8d92affc84519c4c0a6b794058b58607768c8e38ab275de0eb", + "objectId": "0x939337494e5c4fe00e78f120459bf108367938719f717e32c845ab83e762f22c", "version": 1, - "digest": "AvgJes9PZYnWwM9KZmEiBZamurEgezPZUBB2gVJzvEgb" + "digest": "5RBuu4Mz4xV3bQAdU8DQnXLppZtpjGToNcoVujUfpxwG" } }, { "owner": { - "ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000005" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, "reference": { - "objectId": "0x6af2a2b7ca60bf76174adfd3e9c4957f8e937759603182f9b46c7f6c5f19c6d2", + "objectId": "0x94b18ab08b2ddd60e7db0756a605bd31589136eb861b76f49ff3b5ae688f65bc", "version": 1, - "digest": "36ayTWhEK1Ez85fRT6j93sMbQtxo3V55FDsJSnWXTFe5" + "digest": "DXGesd5mWPPaLtwza28GDfumr9NqswcfpbcHp2LaL287" } }, { "owner": { - "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + "ObjectOwner": "0xaca630a6c6d4a9e7a7b033174b3952dfd7c2edeea9fa3a356e97d1a5bc31dae0" }, "reference": { - "objectId": "0x6b517df3c5888ce924d7871e550989f835335c726a43a224ce3df6b6dfa53666", + "objectId": "0x98c27dcdc3cb082f2ae47756648c92ee054721273663cf0ad25d7f2d4fd20128", "version": 1, - "digest": "ARFnQexwF3Q6CDzCgnQfH1v8gY9YAfpXLq4K89FU9koJ" + "digest": "h6NdgxuL5GUffJJjUicoXB1yDDv3qytM1PJFXQQvxdi" } }, { "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "reference": { - "objectId": "0x759bb64d0a576b00a8aae6d439da4a72ef7d8797282c90211bab2bc723cd48e5", + "objectId": "0x9bafea6cfb0a89e0eee6e66ec0d598848067196ed07857012e00e609e8f3ca17", "version": 1, - "digest": "7cuKDvum6Rx2utLqquqYZGr6K3S5xbA2fq88cqs8qjZd" + "digest": "3yh2VHtBQACirSUeoFC6YGUimULgQEAeXVHQYzC8423e" } }, { "owner": { - "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" + "ObjectOwner": "0x7a85e205f86b500a8597a0e3258cfd36cb5e39c04580f5598e501dc31aa55979" }, "reference": { - "objectId": "0x76472cb3b720491fb9a66db21befaed5fbc8528976bc6fb2bc3c5deeef9d114c", + "objectId": "0x9df5ecf6c7c2ed3f31b6ff471aaea04af7d331647c34ab319c8eef45dd043620", "version": 1, - "digest": "zQeqLseHTy2tGhRk4gwvQGoCtUkRd4LqGjKuuvW2Xh1" + "digest": "Fnr1EGFNzyCZgEjLJAYYzHfWrxdLdD1epz2ToST4bTQY" } }, { "owner": { - "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, "reference": { - "objectId": "0x7c9f819b470ae3ac98b6e44eeea9b461d66a38b22a3d2f61ce62c69f68d0c71a", + "objectId": "0x9ee19bb4564baa0449b5160e4dccd02d5d8a54ca794aaba2130db4feb809c1f5", "version": 1, - "digest": "EsxwZdo265ZPbYh99YeTGunSWrMj8dXyNBw2H1E9D9eL" + "digest": "zoUU8mDtdVPRCqfRKWmd5VpMNfgBHd4jVjq4gu8i9vn" } }, { "owner": { - "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" + "ObjectOwner": "0xaca630a6c6d4a9e7a7b033174b3952dfd7c2edeea9fa3a356e97d1a5bc31dae0" }, "reference": { - "objectId": "0x81c7cfd9c7fa81f57a59f14739ee82157fc6a04d48a1b0a6e1187a8898042299", + "objectId": "0xa01451ee51efc5ef58d07e5d373b68187ee3a042553670da8a4dc613adf3ed61", "version": 1, - "digest": "8ESph921vnJz6UaENHxg2ytx68MtT4UoxNpPgWTT99a5" + "digest": "BUizCaTGo3YEdsuDzodmKs9njztBmrQbpNA5DXiLcXZt" } }, { "owner": { - "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" + "ObjectOwner": "0x337b119cf80fabd2c403926df427294e0d1c5444a317a42b1c9dbdbc07375f1b" }, "reference": { - "objectId": "0x85a7aabfb1d8ef14efd87338a58efcdbfaf0cab5e5fc247ccd1080dcdd3d2d7f", + "objectId": "0xa181feb7b518f4e06794c678eace3633c6b1d7222736e9d059e75ca3023b95a0", "version": 1, - "digest": "277zG7Ypo7ruZu1WMxXDUoApdKnUYLBvrws1KkqzHuFm" + "digest": "CYWkB4L1JxphwGcDyzdFZQfNcfbhV33jWxNkRxgcquSH" } }, { "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" }, "reference": { - "objectId": "0x8cd9aca3790437fc6b1b7258c5fefb2e95ffb54134aae1fd04ba757d4286055d", + "objectId": "0xa629b978cb416fdeac008d532329429114c70e49909d97bede0f794b376eafe8", "version": 1, - "digest": "AEXYy8VGP4NX8xpTs7LHiuZYfzTwFiRypmiQrZy9waNY" + "digest": "6Mtsb7P5EAb696MW6fHanVamZe9zSR8kbnAf7kfZd4n8" } }, { "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, "reference": { - "objectId": "0x8fd46357d5ae46f9ee8de7d9b6dfc9da4e497c70dbd2ce5c2fc124658798d5c9", + "objectId": "0xaca6bf6562b4cbbd909e6f9310c23c9517deca84e82d22dfa781c3dd980bbc95", "version": 1, - "digest": "3xUtUi7VT3NzhFGUGsAJbSaimrdSTb2ujoesjNukQjS8" + "digest": "Akr7cJiBjxTYvEm5GttimcB7NVBu8qEU3JEMggT2NkVS" } }, { "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "reference": { - "objectId": "0xa010e1486bfd2f1d7c57a3b2c2edd369e64e8fddc1ca17667314317273cb491d", + "objectId": "0xb12dd88f42ce5db0ba12181deae915d795de5a1e4dfe87deb44a51c8dd8ced7a", "version": 1, - "digest": "DAFo7bkSkk2XRZv88Nb2V9jatTse1TYTqgMmVxVKGRtF" + "digest": "AqgNX8aLoZyCfmq3N3F3kaMTRWpQCQnZ8uzxJvfckbFM" } }, { "owner": { - "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" + "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" }, "reference": { - "objectId": "0xa6e659b1120b373f598d1f6c3214d73abda596eb913857e1fa471ab4420f2e27", + "objectId": "0xc3647488b7f826d5e2602102964e93859bab9374389c1cca8799f91c2b8b1589", "version": 1, - "digest": "E3tr1QPatiEWCjxUNrbKfyRgkNq8tBeiTKFKGyA7g9vk" + "digest": "65ZbHUaDjGRnqwUVGHiSVeHV1Koxpcmk2pPKv9Uj9wYR" } }, { "owner": { - "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" + "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, "reference": { - "objectId": "0xb271c22281276193f817eeab5291b79f5f37698a21323fbafe014e1774c3d22e", + "objectId": "0xcc36edb95e82c14b7175ae99e58c3a27b43fb1ba7d7f1454fbda1ce66e0e5a8f", "version": 1, - "digest": "GjnVg1dHinKTEkLLZYLedhgmLkmNddYhYM9pUZrUDrzc" + "digest": "4X4Mmi3CynwtZsuonDK26HvV4SyQ4d4SykRdn1KLu22a" } }, { "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" }, "reference": { - "objectId": "0xb279227ad10111e5d870facc26d048a05bc440859e718cde0e3bbf257765544c", + "objectId": "0xcf326c0a4f56e81beb81e1ee93cda5bd5bc5e88fa4b730a1909679b2ffb3f0d3", "version": 1, - "digest": "BZaGDacxqx5MFLFm6F3UeBVpHG5hZykqSHFoD1o5F5kq" + "digest": "DcBPndnKeykAEYA7cmTz7PW7u2pg46nUB7JDEG2gEaQj" } }, { "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" }, "reference": { - "objectId": "0xbb43894417b1b4a4cee286133d85cae9706c217d150daf4d12168bc5e9147d78", + "objectId": "0xd3260d5e56e16920f270b3f92a40782447e360ea000aba2af08f0d1f8ad6b5f0", "version": 1, - "digest": "E9CuCY7GpGsGHb6cjKdSrrWc3xmRN3wjp4FqjZ22DmoT" + "digest": "2W8RQHXqNzdkhKPUCmawicG7iHgvR6BXYMCiktweVruJ" } }, { "owner": { - "ObjectOwner": "0xc2bda3bfc534a80abf077c5be16ba9a0832c206ab8e230605d6839b7d87c2038" + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" }, "reference": { - "objectId": "0xc066a3793934a82847a3cee2a0dd84832ba1ba24b71ac0602b1a60cbbe553d17", + "objectId": "0xd43afda73e2bb89e67d8049618176ba09456438f9fcea5f8a3e91f8a1b17ecd8", "version": 1, - "digest": "DWvQzrbs9HQQ411fc6f9QtEFerRLv956sUyRzY1M9HPJ" + "digest": "8PrSccVLMdHmUji4HbGeUvxh8WTHDnzqk75tWz67Tf8d" } }, { @@ -789,19 +836,19 @@ snapshot_kind: text "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, "reference": { - "objectId": "0xc79de55a6454ac8e927645a08e4e9b3ce8e814ce9fc25eb7a347a91fc2228f4f", + "objectId": "0xd8fa8491b91a220af49eb0b54e1d5de725ca40cad8ecca989f27a2f713402490", "version": 1, - "digest": "6avmJYgcDx3Ha6XXNTRCZeHACaaERgC4AkNDCE1a8TGA" + "digest": "F7NGNkMwvuPkKEuvnYgErENvp1HqQfWkMNySiKssSy6b" } }, { "owner": { - "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" }, "reference": { - "objectId": "0xc7ee80d93a34f12ff58b0ceaf16c4e06ee6ceeb547b031b0d3eab1a8a6ca873f", + "objectId": "0xdde2baa8f84257d47c8051b5abd214afb850211d7ee74a99c21698e8bd66f81d", "version": 1, - "digest": "F3PmgDERQbRNn44B6on5QPxNVXCLUn18L33fNgv1cazu" + "digest": "3ue34riJ3GWEcmegp2kzVEpnHgWzamPaCZTx7kpjc5ZL" } }, { @@ -809,29 +856,29 @@ snapshot_kind: text "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "reference": { - "objectId": "0xd1e455eb2c367047a03756eb830bf0d2357b0c69905f0d4f3a648cc81498905e", + "objectId": "0xe0ebce954f5f7a4f32e3fa49fa1b76bc1e56008535860b3fe78387db50f95dbd", "version": 1, - "digest": "C2SeaJLLw3x6ShuvPfND8mXCAvLgNz293iTtu5L1SWCW" + "digest": "kNSMhUBQszVBjveLRYdxX9JEUiLzdV3vCzz9ikaYL1n" } }, { "owner": { - "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" + "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, "reference": { - "objectId": "0xd26171201bf3e074f1d1c694ea6ebc5d95d75b75583c393055efbca3eeb9c629", + "objectId": "0xe360ccacf592e9a2d80c7314d882d2adb850db51fd18572a6baf11a85b233e5d", "version": 1, - "digest": "AVs21Q7q1cFThjfKGkzGSSaKsQCjWo3NtDcBDPCCKM76" + "digest": "A2VPHWvMEQHPWw1m5oX6vVZcAwsmkLyCvotCx4MrNjzG" } }, { "owner": { - "ObjectOwner": "0x8ee7b25e9189b59bcedfe3525114b47d4602ce383d1ddbbc3e49d95134fd8286" + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" }, "reference": { - "objectId": "0xd6090aa14b28b0e907434cd8f8469b24769a80ebd722f18df48fe59e5fd47c0f", + "objectId": "0xe5395355afde6c5624ce706e91738cb766a8324c86ea933f38c3f8efbba38507", "version": 1, - "digest": "4yuGdRvF2jHEwP7yfNE56bWPNY9v4b4wDzDngue7sHxb" + "digest": "DLCXf4UxpB6CMKQ7tSvdpMLZzAj5z2Qfuox7qHNefKqR" } }, { @@ -839,47 +886,47 @@ snapshot_kind: text "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" }, "reference": { - "objectId": "0xe47935c2607151b36e2e7d6971418157d7a8997ab2771ae779f5b6a2230334c9", + "objectId": "0xe8bcfc0181e426b9dfcad3785fb4db1a0d0a2b1fa40c99a36b5fe988d2a2f561", "version": 1, - "digest": "GTBQbGkLPeC4hbkBB6QtzPC7P1zngzPdh2tAyPUnS8c" + "digest": "2eMPqCumhC9U4G5rJmSr1ZKxftCS264Z8csh9zBbe4aQ" } }, { "owner": { - "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" }, "reference": { - "objectId": "0xe54599f1bb4b687498caf1e5212c6eaf0b088dcc8159d283ecf2c6287ede48dc", + "objectId": "0xed9ffc59b7e841e0de23ac1b72c4f04a784e2fef9c46f93eed0afdd14f8d047c", "version": 1, - "digest": "Cs9n9dTaq4n8KjEDib829cYFgPQRsyubTQRzX1upqE6x" + "digest": "69xyHdtHM8S6Voaqv4VHX5p7kxehGDVEAosn236u3EYH" } }, { - "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" - }, + "owner": "Immutable", "reference": { - "objectId": "0xefd24c24d3784edec255308f6fc3619f65902ebd122f5a34b105727994326ce1", + "objectId": "0xeed5eebe436d3b4dedb5d25eff6275c5212fb408ab6e2d1756fd9c56b2af892d", "version": 1, - "digest": "FNNTstnHpGPZCv6PNrv7dcodbfXkA4xiz71ZUtiVKYcY" + "digest": "HyUxFE4PT5azA17c56oxTLkT81weUpxi5ZkoCxefEbyC" } }, { "owner": { - "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" + "ObjectOwner": "0x723bc96ed7aa917e131832dd0c60b9b54bb74f5d6c7c5edb89251db0b2881d58" }, "reference": { - "objectId": "0xfbf79fe230691c666820739add167373a61a7faa858231f42feb961ec8fffcf5", + "objectId": "0xf69faa3aa3015380b78c11aae2ee8eed497bb5ee2555e686b53e375ab90fe8cd", "version": 1, - "digest": "2FRzkPVb45cxXoE68vu5QcfM9T7WypPFu199wSHeXUWj" + "digest": "6e9719u3ETKKnmysHMKE5kJoPcV6qGc8gLkoecxbibcm" } }, { - "owner": "Immutable", + "owner": { + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + }, "reference": { - "objectId": "0xffc04eb3011a2933dd0fbfff34cbd6759dbf999f7e2ac372e08d577c56a459cb", + "objectId": "0xfe1d0d9ef3118023510cdf338ca59ae1d3e88e1ca98a3e1904d61a8620a1ad98", "version": 1, - "digest": "FVzi8BDkddrv8mDQE6DG5JxD7THHn1PKLqzSj5sovdAc" + "digest": "7iwW12UgvrywkA79tKwWSZ5AviKqcgKytqz9dWwZLDoh" } } ], @@ -893,12 +940,12 @@ snapshot_kind: text "digest": "11111111111111111111111111111111" } }, - "eventsDigest": "HvJEc58b44AonQBEvAqrxrG5GmZUBc284MWDDNCirwNT" + "eventsDigest": "652jxdjvsGZDUHS3GoacF4VaCqx614uQNBBNuHSFZsWL" }, "events": [ { "id": { - "txDigest": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "txDigest": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "eventSeq": "0" }, "packageId": "0x000000000000000000000000000000000000000000000000000000000000107a", @@ -906,13 +953,13 @@ snapshot_kind: text "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "type": "0x2::display::DisplayCreated<0x107a::nft::Nft>", "parsedJson": { - "id": "0xffc04eb3011a2933dd0fbfff34cbd6759dbf999f7e2ac372e08d577c56a459cb" + "id": "0xeed5eebe436d3b4dedb5d25eff6275c5212fb408ab6e2d1756fd9c56b2af892d" }, - "bcs": "JDM3MXfy5KRcXneqNjftj86a4vwAgXavnUbYjyJPxxsc" + "bcs": "H5KEdze24AHvBWWazyGUYQRmhE3zFDzH85xKo3PGYFYg" }, { "id": { - "txDigest": "5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", + "txDigest": "E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", "eventSeq": "1" }, "packageId": "0x000000000000000000000000000000000000000000000000000000000000107a", @@ -956,10 +1003,10 @@ snapshot_kind: text } ] }, - "id": "0xffc04eb3011a2933dd0fbfff34cbd6759dbf999f7e2ac372e08d577c56a459cb", + "id": "0xeed5eebe436d3b4dedb5d25eff6275c5212fb408ab6e2d1756fd9c56b2af892d", "version": 1 }, - "bcs": "32c8gaMxAbF5n7wL32Kdpi5pBQy2PGi7t3onffny9jqe2gaDRgL1VDGq8Qdf7iMRM4tBvAxYsetLqrYETRwRew9c7x9XTW9aw4K9dDM1KZfHC5jj2QMRwbEHALCq9vYszTEk8A5F3x1rR1edmQyMMY7mKPRCGoRymgHdDSN6gcN6F7HBf3ya4ybxTpZax5RE4tawq5kbjSJnGNe54KAsAtqMpnJsSJ1PP5Z91r3CkDB3gKAeDah1WxySJMBgdpoJbzcniQJBQQ4ppTBFoe1FBhES9fcsbpKhMNxqyMi19TdopYSpDJHgfhPs1urgbMuNTJR6fNjbDsfY1a1DgeW7QAxpcQENY4gY4qFsWX86W8bC8K4kJYuxEEfcGCrJ16Rj2svNVE9gEeDKeRFfCH6gc9Je35Gd4chRcCjkgDoKcqzyB2vDiUrtCCbRGBrJa77x3y4TmucBM5v7LVs8eha6hEAEYQG5CDQdxTmTR6k" + "bcs": "2tpynDmB6yz6PBwHubkQHtuoUCpW2Ko34tEUKCUYU4hDbWkG2JELHHSrtenbXE8EcA9CHviCfpsdnvYbUpg58KW4EJBsb4tzfqEgPzNRmtWxM29JmEfgYygghy3oRsnjudFohMUgoKCCZbka4UHMvpVqKvEz9CKM9KrSvZFMEz6n2vyCUnuD6WZ7nKuTWmZD9SDKC7sx7VNCcE3iPnfN7gTJELP3UT7zNxWF1HnitZPdVdvgQwFmNsxpX881CXboKPSij3yRiLZauDknV6TDvus5QxB9sP7SioVbYSWMZoTdha1FAqRHWBT9r5buiKVj7WxKZiAm3ijfsVS47GbQ1xV3GSTvngJ4yUTp5TxFcdxRU18LsuvkTdMNwo9rwzyn1aKM3rHxCJ3fQgzBPVZ1Q5ENmHHfHpCxwprrYsktWCKM5RHErbTNog6PyB9D1wchgkNqaGvhFNmRP6bittnM33wBgoJ9MhE9CcADVeU" } ], "objectChanges": [ @@ -967,7 +1014,7 @@ snapshot_kind: text "type": "published", "packageId": "0x0000000000000000000000000000000000000000000000000000000000000001", "version": "1", - "digest": "4JE3kcoZSKseaSsSdgQveyB6GHU2GF3oBpg5pJ39ZSFG", + "digest": "7XoN6TGQbADiyEaVmrD6gmXJDJkX21MxGMc4pYCo9mKN", "modules": [ "address", "ascii", @@ -993,7 +1040,7 @@ snapshot_kind: text "type": "published", "packageId": "0x0000000000000000000000000000000000000000000000000000000000000002", "version": "1", - "digest": "8zNipc5CUAJYs6ak77VrFXPxQyY5vwtkUmyZz8w2j5Fq", + "digest": "5DVdPqNcu7yg8VSoQy9GwmoUprvHyK87jxnbRoac1r1S", "modules": [ "address", "authenticator_state", @@ -1056,7 +1103,7 @@ snapshot_kind: text "type": "published", "packageId": "0x0000000000000000000000000000000000000000000000000000000000000003", "version": "1", - "digest": "GCLhS6D6SeU7JstsXGPEF2PTY9rGQXNLo3A6j162jdFi", + "digest": "B7r86UhqxCjrBYVNGnSf7QBNX7EWWm7kLHZ5ht1MLAre", "modules": [ "genesis", "iota_system", @@ -1082,7 +1129,7 @@ snapshot_kind: text "objectType": "0x3::iota_system::IotaSystemState", "objectId": "0x0000000000000000000000000000000000000000000000000000000000000005", "version": "1", - "digest": "73woKiMAUkS1ejdzSwyD1WLDCxsPVYURbwpMBa64Tu4h" + "digest": "8CWv4f5Nov5aRXTQa2EJk7zsNhfKVbX1fEG8VP6Wsa6d" }, { "type": "created", @@ -1095,7 +1142,7 @@ snapshot_kind: text "objectType": "0x2::clock::Clock", "objectId": "0x0000000000000000000000000000000000000000000000000000000000000006", "version": "1", - "digest": "2iNGKB1PQafgW1o6Y3ZSkUwhxJrNbbEK4PfrqW4zxokW" + "digest": "DjXTupBThwgJcFY9YDL2Xan9bwTTA3BYrW72Dgh1AMJA" }, { "type": "created", @@ -1108,13 +1155,13 @@ snapshot_kind: text "objectType": "0x2::random::Random", "objectId": "0x0000000000000000000000000000000000000000000000000000000000000008", "version": "1", - "digest": "4ZVAMTUCrpGjd97gLUnqTSvk2gqJvr7FnGopRvQkVnzn" + "digest": "aCZvLjShGf5G1AEq3jb9c3y3jxi8NhX17kFUxKwt1q1" }, { "type": "published", "packageId": "0x000000000000000000000000000000000000000000000000000000000000000b", "version": "1", - "digest": "81neBedYgjyePBFEUdm3PPC3K6BLGjwuyyFXxaDFKbAT", + "digest": "7rF3B3o4ZHxju9pxz2eGmYhigdg5MBx4WLkyYcBaMMs9", "modules": [ "bridge", "chain_ids", @@ -1137,13 +1184,13 @@ snapshot_kind: text "objectType": "0x2::deny_list::DenyList", "objectId": "0x0000000000000000000000000000000000000000000000000000000000000403", "version": "1", - "digest": "E7zwAronHAhg8gRcCNJrzffEcMCqyGwTRvXzk6tZYTzG" + "digest": "57nJzNa4jJBPDpbVVKcntb8rEZLhwhAsLAH7o6r6YLAb" }, { "type": "published", "packageId": "0x000000000000000000000000000000000000000000000000000000000000107a", "version": "1", - "digest": "BvAgaA1pE6cMYg7tNZv7sdafwnYCgxk9DPseprtGCXw5", + "digest": "5Ad5TaF7cAZwf8U3rTD19oxAEfrhptBrSg9qGfYf36nY", "modules": [ "address_unlock_condition", "alias", @@ -1163,65 +1210,56 @@ snapshot_kind: text "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" + "AddressOwner": "0x4b2a0b010344ffda7202ecd5f76b742b78516cfcdb208e3314d65a4157654c4b" }, - "objectType": "0x3::validator_cap::UnverifiedValidatorOperationCap", - "objectId": "0x018673f8b9c2eee4dd923fb6fcb3eb77d0e33d15921ac59f1c57ea7dfe4c3f40", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x00d74df673e00106b1a45136cf8cfae1ca792c3de53232e06d09fbf5d4f60b66", "version": "1", - "digest": "AkRRb1Y6sXYHYunTUhbhqZoeTF2KdJ9E2YJh8zncRof3" + "digest": "57LAitmwQMZgioGaRVybfVmZ2Yc57HrJ2Hkm7iGmX5aD" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + "ObjectOwner": "0x1d3c44ed95179381aec577194b10aaaccddc14ceedf533b9b6b4eef5535476e0" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x08e1bd72a9f239c8e99a3ee8f1f4141b48ba063ecc0ebff878152e2b481b94bc", + "objectType": "0x2::dynamic_field::Field<u64, 0x3::staking_pool::PoolTokenExchangeRate>", + "objectId": "0x0214b29e560e3fa6b37836ed7167db0da40a1823cf8b01f0690e1b3bfb4ebdc5", "version": "1", - "digest": "FszgvFokRH5rBBNoLrTxKWBGnCAdiRqXpWYixmwBvwpf" + "digest": "3w6HwgnTmaGDCnAciokuEpZHj2NjFWv6i9KiCHqH7Nct" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x163b3f079eb959dc96768c65497ef5c3b48106439a306324c484a8d28e0ba50f", + "objectId": "0x04942308c8384041a5af2c13c807418d069d3a6e53519dadd1236f0344385a14", "version": "1", - "digest": "2ueUhUo7CYjPNknnKCgASKC7u1ifGVYa9N2GpqNEFzoE" + "digest": "811mkHeY9LoDN16a3CSnr22AZK8R2rJa9DbDwuvDp9kd" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" + "ObjectOwner": "0xee64ad330164aaa798846df2f2df3525410779fb120765a1c5fb1f931bed784b" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x16d23fb8f64faebeea28ecdd43d96cb60ed1c5a297347d61a0c3fa4fff08944e", - "version": "1", - "digest": "ApwvoSkL1WC5UcJWSbzGuNJHn9F5ZPuALucnMNP63sCF" - }, - { - "type": "created", - "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", - "owner": "Immutable", - "objectType": "0x2::coin::CoinMetadata<0x2::iota::IOTA>", - "objectId": "0x1a1f82376293f736136384ae03744241eb257a82f25eb223c7ef02e67442d6df", + "objectType": "0x2::dynamic_field::Field<u64, 0x3::staking_pool::PoolTokenExchangeRate>", + "objectId": "0x137cb52b1bbef8e93857fd3dc67f7f70f2aabc1d7d6eb4afb28e7dea87b9ccf1", "version": "1", - "digest": "CJY1zNjevKH6Kya6aWr3Ri1HQsgeZFCf9qR4HqH72Fnt" + "digest": "9fG99ZbX8SPLLkLnRcah82PyLY3BSbgbLD7HKD6XntZ4" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" + "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x1d8f7a031fcca49d212b5bf2b57704913f7c6ca423357a01dda70a150e8a35b0", + "objectId": "0x2a74fa6e7b6f90225cb36fcdc3e416d1d3d9e209ab14aafff3afd2c3587c9aec", "version": "1", - "digest": "7yn1SCAHPQVAmJANbtPKjkZyzpN675SHdNBNwdFfXGbZ" + "digest": "E37WvCF8d2asqAJH7MtR9rnyiWeHqjgsQqL4gbjAwgLk" }, { "type": "created", @@ -1230,185 +1268,183 @@ snapshot_kind: text "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x1f3119f496e7c38af65bc0f29ee79354de5f554d03d0aa433bee015e280e97b4", + "objectId": "0x2ad996aa647ab628cf294b4d98b24c9debb351e72b4a72ee3425b7d9a41d08a8", "version": "1", - "digest": "59iXLqERWshXAXJqsVuwRRmoxW4AXLv5jnH3cPxRwAXw" + "digest": "F5fPj6gyhcWnNMJTnxis1goTvTNUXYr69zJudZaqMt2t" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", - "owner": { - "AddressOwner": "0x4b2a0b010344ffda7202ecd5f76b742b78516cfcdb208e3314d65a4157654c4b" - }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x2438ca49fb81fc37f74d890207c3a922b27de14b474525b4fa111de094e5194c", + "owner": "Immutable", + "objectType": "0x2::coin::CoinMetadata<0x2::iota::IOTA>", + "objectId": "0x30f537424026a4259959d4c28f4b474558ba881bb517267f9dac6e7bc7518a5d", "version": "1", - "digest": "A5e18Gy41Bxq2McoH7FbiYJFfU1yx26XRgvDk31KbRTz" + "digest": "CF2vkHV4X8YVrhgVKRafUDX7HmSbxSkvmwFYFPkEE4PM" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" + "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" }, "objectType": "0x3::staking_pool::StakedIota", - "objectId": "0x303fac6452e9b844d960dcd60c84efbbd28b017774787c58705b67af946d7076", + "objectId": "0x3ad5e2039f28a1d146d5f7e2094b7e55f1cc35f34a53aa207250d51e76febbb4", "version": "1", - "digest": "D582bFMq1ftKpRhrcFeRLhEJUbfrRZZLA6Ymgs87S6cC" + "digest": "2B9M7nEmFp1jcPM72pd7qXNNenQZwdp5dGu4xoykGeXZ" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" + "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" }, - "objectType": "0x3::staking_pool::StakedIota", - "objectId": "0x3238c6a409ed6951125aaea3334cf632e053e0d09507e8a53d4b4b5923b896d0", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x3e14d7d3f210d79f29a18955f4e98676ebb40e51119ddd6d26a8add99daceb95", "version": "1", - "digest": "GZiR6Qj8jF3vb8Eq3AF3zrCrqGcHpKPFv3MYRCn4RLsT" + "digest": "A6ZSdzLj5Bvv27zaQoFm5rUkS91sK8nJKfntqKbHepUb" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0xce3d02f2a570935f50158318f1f0f8706fb80873afce7bb914625c1082225399" + "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, - "objectType": "0x2::dynamic_field::Field<0x2::object::ID, address>", - "objectId": "0x3873a7b96acdc98bfa6db49a096f8e704caa6dc7cca29aecff52abe11442dc3f", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x3e5742903bb62d61adea88aa26bad2fad57984e3eefc58a2f1810b82228e5117", "version": "1", - "digest": "DmLPuMUMQHtu4hRNpXbcjqtRKWrJqPLUDnZw84GmgaUZ" + "digest": "3MVJSV5RKkXLxsTdHqPQERjnmAQAoxkkiRv1Q6G8zHaU" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0xce3d02f2a570935f50158318f1f0f8706fb80873afce7bb914625c1082225399" + "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, - "objectType": "0x2::dynamic_field::Field<0x2::object::ID, address>", - "objectId": "0x3e5b52bc91e54ac367f5987785c7b0a68869a4be2eb0a74f7bee2172e0a70dbc", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x42cfd74b065c48da45fe6048796ce9cdd3a380c182e7d89573fabc229e3ce4ab", "version": "1", - "digest": "ARd1kECfyCcc3TUTnFZrdYRAGX6xn9G3ZKNtQKNDEA8R" + "digest": "5AzKkrWAReMDgNF2bbiveSDDWvJpQzSX7xHRHNJu9Phy" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" + "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x3fb0323b083da0415032b289cb6e610388de91f9666a67c02df8ddd9edb00f4c", + "objectId": "0x4728ee263c6a7de9fcd0d4d58db500e0ceed99977f44ca323e9a521970efd085", "version": "1", - "digest": "EidyXR5ajBRafVgkEA1cm598AdyK66F2NBub16VyE9aB" + "digest": "781mLWh1jZYNUxtypVJ4XWvHvvga91AZyRfrtXSG53RZ" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, - "objectType": "0x3::staking_pool::StakedIota", - "objectId": "0x4451b2f6d468c331dafa10f4b3fbf057b89f684553bf9298d0c4f8e61df2fa95", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x495af1962cebf47d1eb0cdabb02508114dc8a05c8bb4e6c18b8e81af78c8c4b5", "version": "1", - "digest": "33vUxz6x2MsvDWTQWpFpjWQbPbbZhUxPyn3uwSDfAVQx" + "digest": "A7cf1SRMJYAqceQb78geXHujpHQuiqEggZnWu24enHu1" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0xce3d02f2a570935f50158318f1f0f8706fb80873afce7bb914625c1082225399" + "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" }, - "objectType": "0x2::dynamic_field::Field<0x2::object::ID, address>", - "objectId": "0x4547fa20f95dcc6e221d18eaf933f993ee8f46f98eef1705853a71f9c917c185", + "objectType": "0x3::staking_pool::StakedIota", + "objectId": "0x608863c1d4738cb9829f9bd7c5b3ab15123b6e6eab762dedcfacc23ec23d37c9", "version": "1", - "digest": "J3kRd8hXaaV2mFKES9k7X2NnjxDCGcFAUoXCqsY2reZJ" + "digest": "6NnLvzoYdm9qBrrsgCj3x2vtrQeMceUt1QuBSvxvgXq5" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0xbc4cf4a6e17ba874f668896d13e8dda92bab7a8870b3118d26fd67501028c2f3" + "ObjectOwner": "0xaca630a6c6d4a9e7a7b033174b3952dfd7c2edeea9fa3a356e97d1a5bc31dae0" }, - "objectType": "0x2::dynamic_field::Field<u64, 0x3::staking_pool::PoolTokenExchangeRate>", - "objectId": "0x49315560aecdcf0beba3cfccb2d04f29f5d95b440cb0b6dadec64349b81083d6", + "objectType": "0x2::dynamic_field::Field<0x2::object::ID, address>", + "objectId": "0x660e677bad9d848d1f131d14faef42025eedf0f0e14ede894760e0ebb51a80da", "version": "1", - "digest": "BRyi5NozgWp2Aji5hM8p83M8VgyR3PYg7G27MiYR2B9z" + "digest": "EJiD9QzGf9afkBrdYyCfsg9f7Cx64oSLDTVMuvxc2jb2" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0x672bba3ef273000a1627a5f128022e261e36a85db6c508b917aa3139a2e9720d" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, - "objectType": "0x2::dynamic_field::Field<u64, 0x3::staking_pool::PoolTokenExchangeRate>", - "objectId": "0x497d698dce0d808d05cb3543b2decd461eb67a0a832acf7e3d822ff61663acb4", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x6689ff8b1b4ce68bde6f39ebd1031e0339a4036a66b7d0a85c366ac0e6817748", "version": "1", - "digest": "242fQBuczzCLdXuMZ7FZwR9m86DacJxUTC5xuUW62DSG" + "digest": "GYs13Dvcx4XAj76Q4fYfXEgFTyUvVfZYorzvFv8KBRrc" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" + "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, - "objectType": "0x3::validator_cap::UnverifiedValidatorOperationCap", - "objectId": "0x4a4a868c023e670163f57e2467cccdaedef147daf5170fc9b0ce7caa48284a76", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x69cd03a3cc6b2e4a1eae6bef4892f642d8f87ed3d83b758cc5242034bd36f565", "version": "1", - "digest": "AWztWHzVjwd5rYXr7ork1gYC54JqtGpwHNdFA9i6gA16" + "digest": "FSfz1Dtc26vvg9ASRGoVHGg1WVbVqLcBtvrcYtTt6bLe" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" + "ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000005" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x4df66b7aab12f518bfba67b0081cadb8a321870b171a98d2aceafb6804e3b003", + "objectType": "0x2::dynamic_field::Field<u64, 0x3::iota_system_state_inner::IotaSystemStateV1>", + "objectId": "0x6af2a2b7ca60bf76174adfd3e9c4957f8e937759603182f9b46c7f6c5f19c6d2", "version": "1", - "digest": "EPxkpuZZYcjDJq9AzNLMPM8vsY1URjow7nmDuxQQ1QCb" + "digest": "Er8zLC9VrCrgZZcnhjLkA73mZ3J63rxiboYV6EsqmhN2" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0xce3d02f2a570935f50158318f1f0f8706fb80873afce7bb914625c1082225399" + "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" }, - "objectType": "0x2::dynamic_field::Field<0x2::object::ID, address>", - "objectId": "0x50948c5a6a706b267a6b2c2a4a2043d28ba11c5e022198f58be4182d65987a2d", + "objectType": "0x3::staking_pool::StakedIota", + "objectId": "0x6b2a75a39565fe93f6f2694e79274205ec65dac6aecff0a38b3c3947e2c9e561", "version": "1", - "digest": "9SGZtrm6U2rsCqiaFoVQEUSreYstnrCRg6f2EzwKGHq" + "digest": "Hy3XSG83B625ywGh161TYQ2VqBJzKxFccgp1fL3Zjbob" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" }, - "objectType": "0x3::validator_cap::UnverifiedValidatorOperationCap", - "objectId": "0x58efd4d54b74b390e2d3bbe785ec5df555c366b1a6eb616fb07c44d6c3f67c8c", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x82054bc6567488b4924203907f0bc62f3f789e1f5edc901798c5a58042dfa1a8", "version": "1", - "digest": "FbrF4mnrw5oXKX1trYT2NoENq7CaSGVZfXj5XitjSqhH" + "digest": "DRYTDtLfuoxayHMXVqxuuWvv4DJnjgZQvoZebdCoGszX" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x5b0e7d31f9c3a421f94dae3511983a2be67e04a7071171da2380f82be103239f", + "objectId": "0x8a23c86abe72190400e37fd3affc876000ad1302712cbdaf76077f7d541b6400", "version": "1", - "digest": "6s1xQf5HR8HMLNaR1QxPcfV4Ys8AL94UUg7cRpD6r4WD" + "digest": "2fqjooDKXPoqAFBjzh6cGr53zuTfngNLuHzJoPoR7uvk" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0x1dd0079cb68a9c0dc5cbbb0c9c5d831bb56ed88c4a12c647f1352832092d11dc" + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" }, - "objectType": "0x2::dynamic_field::Field<u64, 0x2::random::RandomInner>", - "objectId": "0x5c995a223cf5e88e2d19935481d2d06b2e55977cb3615ad6784cc360a42d6501", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x9045719b475148ea5e0547b2a0dd8f8aaa19a658deac6bfca7a184d1dfa13d87", "version": "1", - "digest": "2ih9uvSzKBSv9YhrNZdMokhPcZgZ8gTt48TyUToB6JgX" + "digest": "9puPcXBGsDq9hTfkFMUHpmruAkuqsqQmBt6b2ChxRU2f" }, { "type": "created", @@ -1417,53 +1453,53 @@ snapshot_kind: text "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x5ee269cea868091ea1c5da5f15581c2f7b1708413b3bdda3e4c9528f2b3a5bb9", + "objectId": "0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", "version": "1", - "digest": "7mvhi6RxxMnPohJzMnCYoLHBTLUcRtwFtBXe1ZoanE6D" + "digest": "5B8YV4P7Q93phJVPgm8MVZzsoXdSxFnKLAjAkMnLYcQb" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" + "ObjectOwner": "0xaca630a6c6d4a9e7a7b033174b3952dfd7c2edeea9fa3a356e97d1a5bc31dae0" }, - "objectType": "0x3::validator_cap::UnverifiedValidatorOperationCap", - "objectId": "0x61f2f500cb861d8d92affc84519c4c0a6b794058b58607768c8e38ab275de0eb", + "objectType": "0x2::dynamic_field::Field<0x2::object::ID, address>", + "objectId": "0x9254a40663ad7e72d73dd1f04b1eca4e0486cc9f54cdd9a7c3d4e479a8131aa0", "version": "1", - "digest": "AvgJes9PZYnWwM9KZmEiBZamurEgezPZUBB2gVJzvEgb" + "digest": "Du8gWCJQvqcsNBEpSYTDSB2aaRyVEG8h7niCPhKQtZFf" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000005" + "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, - "objectType": "0x2::dynamic_field::Field<u64, 0x3::iota_system_state_inner::IotaSystemStateV1>", - "objectId": "0x6af2a2b7ca60bf76174adfd3e9c4957f8e937759603182f9b46c7f6c5f19c6d2", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0x939337494e5c4fe00e78f120459bf108367938719f717e32c845ab83e762f22c", "version": "1", - "digest": "36ayTWhEK1Ez85fRT6j93sMbQtxo3V55FDsJSnWXTFe5" + "digest": "5RBuu4Mz4xV3bQAdU8DQnXLppZtpjGToNcoVujUfpxwG" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x6b517df3c5888ce924d7871e550989f835335c726a43a224ce3df6b6dfa53666", + "objectId": "0x94b18ab08b2ddd60e7db0756a605bd31589136eb861b76f49ff3b5ae688f65bc", "version": "1", - "digest": "ARFnQexwF3Q6CDzCgnQfH1v8gY9YAfpXLq4K89FU9koJ" + "digest": "DXGesd5mWPPaLtwza28GDfumr9NqswcfpbcHp2LaL287" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "ObjectOwner": "0xaca630a6c6d4a9e7a7b033174b3952dfd7c2edeea9fa3a356e97d1a5bc31dae0" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x759bb64d0a576b00a8aae6d439da4a72ef7d8797282c90211bab2bc723cd48e5", + "objectType": "0x2::dynamic_field::Field<0x2::object::ID, address>", + "objectId": "0x98c27dcdc3cb082f2ae47756648c92ee054721273663cf0ad25d7f2d4fd20128", "version": "1", - "digest": "7cuKDvum6Rx2utLqquqYZGr6K3S5xbA2fq88cqs8qjZd" + "digest": "h6NdgxuL5GUffJJjUicoXB1yDDv3qytM1PJFXQQvxdi" }, { "type": "created", @@ -1472,141 +1508,141 @@ snapshot_kind: text "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x76472cb3b720491fb9a66db21befaed5fbc8528976bc6fb2bc3c5deeef9d114c", + "objectId": "0x9bafea6cfb0a89e0eee6e66ec0d598848067196ed07857012e00e609e8f3ca17", "version": "1", - "digest": "zQeqLseHTy2tGhRk4gwvQGoCtUkRd4LqGjKuuvW2Xh1" + "digest": "3yh2VHtBQACirSUeoFC6YGUimULgQEAeXVHQYzC8423e" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" + "ObjectOwner": "0x7a85e205f86b500a8597a0e3258cfd36cb5e39c04580f5598e501dc31aa55979" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x7c9f819b470ae3ac98b6e44eeea9b461d66a38b22a3d2f61ce62c69f68d0c71a", + "objectType": "0x2::dynamic_field::Field<u64, 0x3::staking_pool::PoolTokenExchangeRate>", + "objectId": "0x9df5ecf6c7c2ed3f31b6ff471aaea04af7d331647c34ab319c8eef45dd043620", "version": "1", - "digest": "EsxwZdo265ZPbYh99YeTGunSWrMj8dXyNBw2H1E9D9eL" + "digest": "Fnr1EGFNzyCZgEjLJAYYzHfWrxdLdD1epz2ToST4bTQY" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" + "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x81c7cfd9c7fa81f57a59f14739ee82157fc6a04d48a1b0a6e1187a8898042299", + "objectId": "0x9ee19bb4564baa0449b5160e4dccd02d5d8a54ca794aaba2130db4feb809c1f5", "version": "1", - "digest": "8ESph921vnJz6UaENHxg2ytx68MtT4UoxNpPgWTT99a5" + "digest": "zoUU8mDtdVPRCqfRKWmd5VpMNfgBHd4jVjq4gu8i9vn" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" + "ObjectOwner": "0xaca630a6c6d4a9e7a7b033174b3952dfd7c2edeea9fa3a356e97d1a5bc31dae0" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x85a7aabfb1d8ef14efd87338a58efcdbfaf0cab5e5fc247ccd1080dcdd3d2d7f", + "objectType": "0x2::dynamic_field::Field<0x2::object::ID, address>", + "objectId": "0xa01451ee51efc5ef58d07e5d373b68187ee3a042553670da8a4dc613adf3ed61", "version": "1", - "digest": "277zG7Ypo7ruZu1WMxXDUoApdKnUYLBvrws1KkqzHuFm" + "digest": "BUizCaTGo3YEdsuDzodmKs9njztBmrQbpNA5DXiLcXZt" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "ObjectOwner": "0x337b119cf80fabd2c403926df427294e0d1c5444a317a42b1c9dbdbc07375f1b" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x8cd9aca3790437fc6b1b7258c5fefb2e95ffb54134aae1fd04ba757d4286055d", + "objectType": "0x2::dynamic_field::Field<u64, 0x3::staking_pool::PoolTokenExchangeRate>", + "objectId": "0xa181feb7b518f4e06794c678eace3633c6b1d7222736e9d059e75ca3023b95a0", "version": "1", - "digest": "AEXYy8VGP4NX8xpTs7LHiuZYfzTwFiRypmiQrZy9waNY" + "digest": "CYWkB4L1JxphwGcDyzdFZQfNcfbhV33jWxNkRxgcquSH" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "AddressOwner": "0x7a9730f69d7c9eed4058b7e3ad9019adffdf63a94ef73cd2fb0217f3b9eb6eb1" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0x8fd46357d5ae46f9ee8de7d9b6dfc9da4e497c70dbd2ce5c2fc124658798d5c9", + "objectType": "0x3::validator_cap::UnverifiedValidatorOperationCap", + "objectId": "0xa629b978cb416fdeac008d532329429114c70e49909d97bede0f794b376eafe8", "version": "1", - "digest": "3xUtUi7VT3NzhFGUGsAJbSaimrdSTb2ujoesjNukQjS8" + "digest": "6Mtsb7P5EAb696MW6fHanVamZe9zSR8kbnAf7kfZd4n8" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xa010e1486bfd2f1d7c57a3b2c2edd369e64e8fddc1ca17667314317273cb491d", + "objectId": "0xaca6bf6562b4cbbd909e6f9310c23c9517deca84e82d22dfa781c3dd980bbc95", "version": "1", - "digest": "DAFo7bkSkk2XRZv88Nb2V9jatTse1TYTqgMmVxVKGRtF" + "digest": "Akr7cJiBjxTYvEm5GttimcB7NVBu8qEU3JEMggT2NkVS" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" + "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xa6e659b1120b373f598d1f6c3214d73abda596eb913857e1fa471ab4420f2e27", + "objectId": "0xb12dd88f42ce5db0ba12181deae915d795de5a1e4dfe87deb44a51c8dd8ced7a", "version": "1", - "digest": "E3tr1QPatiEWCjxUNrbKfyRgkNq8tBeiTKFKGyA7g9vk" + "digest": "AqgNX8aLoZyCfmq3N3F3kaMTRWpQCQnZ8uzxJvfckbFM" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" + "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xb271c22281276193f817eeab5291b79f5f37698a21323fbafe014e1774c3d22e", + "objectType": "0x3::validator_cap::UnverifiedValidatorOperationCap", + "objectId": "0xc3647488b7f826d5e2602102964e93859bab9374389c1cca8799f91c2b8b1589", "version": "1", - "digest": "GjnVg1dHinKTEkLLZYLedhgmLkmNddYhYM9pUZrUDrzc" + "digest": "65ZbHUaDjGRnqwUVGHiSVeHV1Koxpcmk2pPKv9Uj9wYR" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x2868fed4dbeb23d2ace3ee3ad6e39061423c5692a2b289b39c643c0baf2d8d85" + "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xb279227ad10111e5d870facc26d048a05bc440859e718cde0e3bbf257765544c", + "objectId": "0xcc36edb95e82c14b7175ae99e58c3a27b43fb1ba7d7f1454fbda1ce66e0e5a8f", "version": "1", - "digest": "BZaGDacxqx5MFLFm6F3UeBVpHG5hZykqSHFoD1o5F5kq" + "digest": "4X4Mmi3CynwtZsuonDK26HvV4SyQ4d4SykRdn1KLu22a" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "AddressOwner": "0x0b37c2167535af8a5729c44690f7be89eb248a6eb3cda5fd37ee1029949f0c29" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xbb43894417b1b4a4cee286133d85cae9706c217d150daf4d12168bc5e9147d78", + "objectId": "0xcf326c0a4f56e81beb81e1ee93cda5bd5bc5e88fa4b730a1909679b2ffb3f0d3", "version": "1", - "digest": "E9CuCY7GpGsGHb6cjKdSrrWc3xmRN3wjp4FqjZ22DmoT" + "digest": "DcBPndnKeykAEYA7cmTz7PW7u2pg46nUB7JDEG2gEaQj" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0xc2bda3bfc534a80abf077c5be16ba9a0832c206ab8e230605d6839b7d87c2038" + "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" }, - "objectType": "0x2::dynamic_field::Field<u64, 0x3::staking_pool::PoolTokenExchangeRate>", - "objectId": "0xc066a3793934a82847a3cee2a0dd84832ba1ba24b71ac0602b1a60cbbe553d17", + "objectType": "0x3::validator_cap::UnverifiedValidatorOperationCap", + "objectId": "0xd3260d5e56e16920f270b3f92a40782447e360ea000aba2af08f0d1f8ad6b5f0", "version": "1", - "digest": "DWvQzrbs9HQQ411fc6f9QtEFerRLv956sUyRzY1M9HPJ" + "digest": "2W8RQHXqNzdkhKPUCmawicG7iHgvR6BXYMCiktweVruJ" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xc79de55a6454ac8e927645a08e4e9b3ce8e814ce9fc25eb7a347a91fc2228f4f", + "objectId": "0xd43afda73e2bb89e67d8049618176ba09456438f9fcea5f8a3e91f8a1b17ecd8", "version": "1", - "digest": "6avmJYgcDx3Ha6XXNTRCZeHACaaERgC4AkNDCE1a8TGA" + "digest": "8PrSccVLMdHmUji4HbGeUvxh8WTHDnzqk75tWz67Tf8d" }, { "type": "created", @@ -1615,95 +1651,106 @@ snapshot_kind: text "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xc7ee80d93a34f12ff58b0ceaf16c4e06ee6ceeb547b031b0d3eab1a8a6ca873f", + "objectId": "0xd8fa8491b91a220af49eb0b54e1d5de725ca40cad8ecca989f27a2f713402490", "version": "1", - "digest": "F3PmgDERQbRNn44B6on5QPxNVXCLUn18L33fNgv1cazu" + "digest": "F7NGNkMwvuPkKEuvnYgErENvp1HqQfWkMNySiKssSy6b" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" + "AddressOwner": "0x20227cfbc6699debf187d2d7bca3d253cd50f87c165516d3dc53a246a9af7182" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xd1e455eb2c367047a03756eb830bf0d2357b0c69905f0d4f3a648cc81498905e", + "objectType": "0x3::staking_pool::StakedIota", + "objectId": "0xdde2baa8f84257d47c8051b5abd214afb850211d7ee74a99c21698e8bd66f81d", "version": "1", - "digest": "C2SeaJLLw3x6ShuvPfND8mXCAvLgNz293iTtu5L1SWCW" + "digest": "3ue34riJ3GWEcmegp2kzVEpnHgWzamPaCZTx7kpjc5ZL" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" + "AddressOwner": "0x2f1c55f14bcab32b91bccbf9909aa2818d644d4a1f6401c37f97383e4d8275ca" }, - "objectType": "0x3::staking_pool::StakedIota", - "objectId": "0xd26171201bf3e074f1d1c694ea6ebc5d95d75b75583c393055efbca3eeb9c629", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0xe0ebce954f5f7a4f32e3fa49fa1b76bc1e56008535860b3fe78387db50f95dbd", "version": "1", - "digest": "AVs21Q7q1cFThjfKGkzGSSaKsQCjWo3NtDcBDPCCKM76" + "digest": "kNSMhUBQszVBjveLRYdxX9JEUiLzdV3vCzz9ikaYL1n" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "ObjectOwner": "0x8ee7b25e9189b59bcedfe3525114b47d4602ce383d1ddbbc3e49d95134fd8286" + "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" }, - "objectType": "0x2::dynamic_field::Field<u64, 0x3::staking_pool::PoolTokenExchangeRate>", - "objectId": "0xd6090aa14b28b0e907434cd8f8469b24769a80ebd722f18df48fe59e5fd47c0f", + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0xe360ccacf592e9a2d80c7314d882d2adb850db51fd18572a6baf11a85b233e5d", "version": "1", - "digest": "4yuGdRvF2jHEwP7yfNE56bWPNY9v4b4wDzDngue7sHxb" + "digest": "A2VPHWvMEQHPWw1m5oX6vVZcAwsmkLyCvotCx4MrNjzG" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xe47935c2607151b36e2e7d6971418157d7a8997ab2771ae779f5b6a2230334c9", + "objectId": "0xe5395355afde6c5624ce706e91738cb766a8324c86ea933f38c3f8efbba38507", "version": "1", - "digest": "GTBQbGkLPeC4hbkBB6QtzPC7P1zngzPdh2tAyPUnS8c" + "digest": "DLCXf4UxpB6CMKQ7tSvdpMLZzAj5z2Qfuox7qHNefKqR" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x4b578d914e64de1ca47bead61bcfa0932646e16de6063089810cfa904eb8b06f" + "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" }, "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xe54599f1bb4b687498caf1e5212c6eaf0b088dcc8159d283ecf2c6287ede48dc", + "objectId": "0xe8bcfc0181e426b9dfcad3785fb4db1a0d0a2b1fa40c99a36b5fe988d2a2f561", "version": "1", - "digest": "Cs9n9dTaq4n8KjEDib829cYFgPQRsyubTQRzX1upqE6x" + "digest": "2eMPqCumhC9U4G5rJmSr1ZKxftCS264Z8csh9zBbe4aQ" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + "AddressOwner": "0x7dbda9c2efa8255eea64cf28b64294ffed6af432f53b661d24f3807895ee828d" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xefd24c24d3784edec255308f6fc3619f65902ebd122f5a34b105727994326ce1", + "objectType": "0x3::validator_cap::UnverifiedValidatorOperationCap", + "objectId": "0xed9ffc59b7e841e0de23ac1b72c4f04a784e2fef9c46f93eed0afdd14f8d047c", "version": "1", - "digest": "FNNTstnHpGPZCv6PNrv7dcodbfXkA4xiz71ZUtiVKYcY" + "digest": "69xyHdtHM8S6Voaqv4VHX5p7kxehGDVEAosn236u3EYH" + }, + { + "type": "created", + "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", + "owner": "Immutable", + "objectType": "0x2::display::Display<0x107a::nft::Nft>", + "objectId": "0xeed5eebe436d3b4dedb5d25eff6275c5212fb408ab6e2d1756fd9c56b2af892d", + "version": "1", + "digest": "HyUxFE4PT5azA17c56oxTLkT81weUpxi5ZkoCxefEbyC" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", "owner": { - "AddressOwner": "0x44f42943b20151d6a0c3cb0f84006b3441bb3378c668a6df71fa36cb441538c8" + "ObjectOwner": "0x723bc96ed7aa917e131832dd0c60b9b54bb74f5d6c7c5edb89251db0b2881d58" }, - "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", - "objectId": "0xfbf79fe230691c666820739add167373a61a7faa858231f42feb961ec8fffcf5", + "objectType": "0x2::dynamic_field::Field<u64, 0x2::random::RandomInner>", + "objectId": "0xf69faa3aa3015380b78c11aae2ee8eed497bb5ee2555e686b53e375ab90fe8cd", "version": "1", - "digest": "2FRzkPVb45cxXoE68vu5QcfM9T7WypPFu199wSHeXUWj" + "digest": "6e9719u3ETKKnmysHMKE5kJoPcV6qGc8gLkoecxbibcm" }, { "type": "created", "sender": "0x0000000000000000000000000000000000000000000000000000000000000000", - "owner": "Immutable", - "objectType": "0x2::display::Display<0x107a::nft::Nft>", - "objectId": "0xffc04eb3011a2933dd0fbfff34cbd6759dbf999f7e2ac372e08d577c56a459cb", + "owner": { + "AddressOwner": "0xb4b32db0fb2bc9170f796efb2439aeaf46fd0dc0b79af4e12d14bb87c3e9aa65" + }, + "objectType": "0x2::coin::Coin<0x2::iota::IOTA>", + "objectId": "0xfe1d0d9ef3118023510cdf338ca59ae1d3e88e1ca98a3e1904d61a8620a1ad98", "version": "1", - "digest": "FVzi8BDkddrv8mDQE6DG5JxD7THHn1PKLqzSj5sovdAc" + "digest": "7iwW12UgvrywkA79tKwWSZ5AviKqcgKytqz9dWwZLDoh" } ], "timestampMs": "1641175496000", diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000001 b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000001 new file mode 100644 index 00000000000..6aae6016489 Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000001 differ diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000002 b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000002 new file mode 100644 index 00000000000..6e14adb5fdf Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000002 differ diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000003 b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000003 new file mode 100644 index 00000000000..ddf88e95212 Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x0000000000000000000000000000000000000000000000000000000000000003 differ diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/2/0x000000000000000000000000000000000000000000000000000000000000000b b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x000000000000000000000000000000000000000000000000000000000000000b new file mode 100644 index 00000000000..a28b3206877 Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x000000000000000000000000000000000000000000000000000000000000000b differ diff --git a/crates/iota-framework-snapshot/bytecode_snapshot/2/0x000000000000000000000000000000000000000000000000000000000000107a b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x000000000000000000000000000000000000000000000000000000000000107a new file mode 100644 index 00000000000..56d912f6c89 Binary files /dev/null and b/crates/iota-framework-snapshot/bytecode_snapshot/2/0x000000000000000000000000000000000000000000000000000000000000107a differ diff --git a/crates/iota-framework-snapshot/manifest.json b/crates/iota-framework-snapshot/manifest.json index 71466febef4..261265acff2 100644 --- a/crates/iota-framework-snapshot/manifest.json +++ b/crates/iota-framework-snapshot/manifest.json @@ -8,5 +8,15 @@ "0x000000000000000000000000000000000000000000000000000000000000000b", "0x000000000000000000000000000000000000000000000000000000000000107a" ] + }, + "2": { + "git_revision": "f20be52f200b", + "package_ids": [ + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x000000000000000000000000000000000000000000000000000000000000000b", + "0x000000000000000000000000000000000000000000000000000000000000107a" + ] } } \ No newline at end of file diff --git a/crates/iota-framework/packages/iota-system/sources/iota_system_state_inner.move b/crates/iota-framework/packages/iota-system/sources/iota_system_state_inner.move index 5e701a31961..2097871e915 100644 --- a/crates/iota-framework/packages/iota-system/sources/iota_system_state_inner.move +++ b/crates/iota-framework/packages/iota-system/sources/iota_system_state_inner.move @@ -675,6 +675,9 @@ module iota_system::iota_system_state_inner { let storage_charge_value = storage_charge.value(); let computation_charge = computation_reward.value(); + // Mints or burns tokens depending on the target reward. + // Since not all rewards are distributed in case of slashed validators, + // tokens might be minted here and burnt in the same epoch change. let (mut total_validator_rewards, minted_tokens_amount, mut burnt_tokens_amount) = match_computation_reward_to_target_reward( validator_target_reward, computation_reward, @@ -701,7 +704,7 @@ module iota_system::iota_system_state_inner { let new_total_stake = self.validators.total_stake(); let remaining_validator_rewards_amount_after_distribution = total_validator_rewards.value(); - let total_validator_rewards_distributed = total_validator_rewards_amount_before_distribution - remaining_validator_rewards_amount_after_distribution; + let total_stake_rewards_distributed = total_validator_rewards_amount_before_distribution - remaining_validator_rewards_amount_after_distribution; self.protocol_version = next_protocol_version; @@ -731,9 +734,9 @@ module iota_system::iota_system_state_inner { storage_rebate: storage_rebate_amount, storage_fund_balance: self.storage_fund.total_balance(), total_gas_fees: computation_charge, - total_stake_rewards_distributed: total_validator_rewards_distributed, + total_stake_rewards_distributed, burnt_tokens_amount, - minted_tokens_amount + minted_tokens_amount, } ); self.safe_mode = false; @@ -751,24 +754,22 @@ module iota_system::iota_system_state_inner { /// and the amount of computation fees burned in this epoch. fun match_computation_reward_to_target_reward( validator_target_reward: u64, - mut computation_reward: Balance<IOTA>, + mut computation_charges: Balance<IOTA>, iota_treasury_cap: &mut iota::iota::IotaTreasuryCap, ctx: &TxContext, ): (Balance<IOTA>, u64, u64) { - let mut burnt_tokens_amount = 0; - let mut minted_tokens_amount = 0; - if (computation_reward.value() < validator_target_reward) { - let tokens_to_mint = validator_target_reward - computation_reward.value(); - let new_tokens = iota_treasury_cap.mint_balance(tokens_to_mint, ctx); - minted_tokens_amount = new_tokens.value(); - computation_reward.join(new_tokens); - } else if (computation_reward.value() > validator_target_reward) { - let tokens_to_burn = computation_reward.value() - validator_target_reward; - let rewards_to_burn = computation_reward.split(tokens_to_burn); - burnt_tokens_amount = rewards_to_burn.value(); - iota_treasury_cap.burn_balance(rewards_to_burn, ctx); + let burnt_tokens_amount = computation_charges.value(); + let minted_tokens_amount = validator_target_reward; + if (burnt_tokens_amount < minted_tokens_amount) { + let actual_amount_to_mint = minted_tokens_amount - burnt_tokens_amount; + let balance_to_mint = iota_treasury_cap.mint_balance(actual_amount_to_mint, ctx); + computation_charges.join(balance_to_mint); + } else if (burnt_tokens_amount > minted_tokens_amount) { + let actual_amount_to_burn = burnt_tokens_amount - minted_tokens_amount; + let balance_to_burn = computation_charges.split(actual_amount_to_burn); + iota_treasury_cap.burn_balance(balance_to_burn, ctx); }; - (computation_reward, minted_tokens_amount, burnt_tokens_amount) + (computation_charges, minted_tokens_amount, burnt_tokens_amount) } /// Return the current epoch number. Useful for applications that need a coarse-grained concept of time, diff --git a/crates/iota-framework/packages/iota-system/sources/validator_set.move b/crates/iota-framework/packages/iota-system/sources/validator_set.move index b0579a00205..8c8215e8ba8 100644 --- a/crates/iota-framework/packages/iota-system/sources/validator_set.move +++ b/crates/iota-framework/packages/iota-system/sources/validator_set.move @@ -11,7 +11,7 @@ module iota_system::validator_set { use iota_system::staking_pool::{PoolTokenExchangeRate, StakedIota, pool_id}; use iota::priority_queue as pq; use iota::vec_map::{Self, VecMap}; - use iota::vec_set::VecSet; + use iota::vec_set::{Self, VecSet}; use iota::table::{Self, Table}; use iota::event; use iota::table_vec::{Self, TableVec}; @@ -341,28 +341,14 @@ module iota_system::validator_set { // punished. let slashed_validators = compute_slashed_validators(self, *validator_report_records); - let total_slashed_validator_voting_power = sum_voting_power_by_addresses(&self.active_validators, &slashed_validators); - - // Compute the reward adjustments of slashed validators, to be taken into - // account in adjusted reward computation. - let (total_staking_reward_adjustment, individual_staking_reward_adjustments) = - compute_reward_adjustments( - get_validator_indices(&self.active_validators, &slashed_validators), - reward_slashing_rate, - &unadjusted_staking_reward_amounts, - ); - - // Compute the adjusted amounts of stake each validator should get given the tallying rule - // reward adjustments we computed before. + // Compute the adjusted amounts of stake each validator should get according to the tallying rule. // `compute_adjusted_reward_distribution` must be called before `distribute_reward` and `adjust_stake_and_gas_price` to // make sure we are using the current epoch's stake information to compute reward distribution. let adjusted_staking_reward_amounts = compute_adjusted_reward_distribution( &self.active_validators, - total_voting_power, - total_slashed_validator_voting_power, unadjusted_staking_reward_amounts, - total_staking_reward_adjustment, - individual_staking_reward_adjustments, + get_validator_indices_set(&self.active_validators, &slashed_validators), + reward_slashing_rate, ); // Distribute the rewards before adjusting stake so that we immediately start compounding @@ -638,18 +624,17 @@ module iota_system::validator_set { option::none() } - - /// Given a vector of validator addresses, return their indices in the validator set. + /// Given a vector of validator addresses, return a set of all indices of the validators. /// Aborts if any address isn't in the given validator set. - fun get_validator_indices(validators: &vector<ValidatorV1>, validator_addresses: &vector<address>): vector<u64> { + fun get_validator_indices_set(validators: &vector<ValidatorV1>, validator_addresses: &vector<address>): VecSet<u64> { let length = validator_addresses.length(); let mut i = 0; - let mut res = vector[]; + let mut res = vec_set::empty(); while (i < length) { let addr = validator_addresses[i]; let index_opt = find_validator(validators, addr); assert!(index_opt.is_some(), ENotAValidator); - res.push_back(index_opt.destroy_some()); + res.insert(index_opt.destroy_some()); i = i + 1; }; res @@ -941,35 +926,6 @@ module iota_system::validator_set { } } - /// Compute both the individual reward adjustments and total reward adjustment for staking rewards. - fun compute_reward_adjustments( - mut slashed_validator_indices: vector<u64>, - reward_slashing_rate: u64, - unadjusted_staking_reward_amounts: &vector<u64>, - ): ( - u64, // sum of staking reward adjustments - VecMap<u64, u64>, // mapping of individual validator's staking reward adjustment from index -> amount - ) { - let mut total_staking_reward_adjustment = 0; - let mut individual_staking_reward_adjustments = vec_map::empty(); - - while (!slashed_validator_indices.is_empty()) { - let validator_index = slashed_validator_indices.pop_back(); - - // Use the slashing rate to compute the amount of staking rewards slashed from this punished validator. - let unadjusted_staking_reward = unadjusted_staking_reward_amounts[validator_index]; - let staking_reward_adjustment_u128 = - unadjusted_staking_reward as u128 * (reward_slashing_rate as u128) - / BASIS_POINT_DENOMINATOR; - - // Insert into individual mapping and record into the total adjustment sum. - individual_staking_reward_adjustments.insert(validator_index, staking_reward_adjustment_u128 as u64); - total_staking_reward_adjustment = total_staking_reward_adjustment + (staking_reward_adjustment_u128 as u64); - }; - - (total_staking_reward_adjustment, individual_staking_reward_adjustments) - } - /// Process the validator report records of the epoch and return the addresses of the /// non-performant validators according to the input threshold. fun compute_slashed_validators( @@ -1023,44 +979,37 @@ module iota_system::validator_set { /// The staking rewards are shared with the stakers. fun compute_adjusted_reward_distribution( validators: &vector<ValidatorV1>, - total_voting_power: u64, - total_slashed_validator_voting_power: u64, unadjusted_staking_reward_amounts: vector<u64>, - total_staking_reward_adjustment: u64, - individual_staking_reward_adjustments: VecMap<u64, u64>, + slashed_validator_indices_set: VecSet<u64>, + reward_slashing_rate: u64, ): vector<u64> { - let total_unslashed_validator_voting_power = total_voting_power - total_slashed_validator_voting_power; let mut adjusted_staking_reward_amounts = vector[]; - + + // Loop through each validator and adjust rewards as necessary let length = validators.length(); - let mut i = 0; while (i < length) { - let validator = &validators[i]; - // Integer divisions will truncate the results. Because of this, we expect that at the end - // there will be some reward remaining in `total_reward`. - // Use u128 to avoid multiplication overflow. - let voting_power = validator.voting_power() as u128; - - // Compute adjusted staking reward. let unadjusted_staking_reward_amount = unadjusted_staking_reward_amounts[i]; - let adjusted_staking_reward_amount = - // If the validator is one of the slashed ones, then subtract the adjustment. - if (individual_staking_reward_adjustments.contains(&i)) { - let adjustment = individual_staking_reward_adjustments[&i]; - unadjusted_staking_reward_amount - adjustment - } else { - // Otherwise the slashed rewards should be distributed among the unslashed - // validators so add the corresponding adjustment. - let adjustment = total_staking_reward_adjustment as u128 * voting_power - / (total_unslashed_validator_voting_power as u128); - unadjusted_staking_reward_amount + (adjustment as u64) - }; + + // Check if the validator is slashed + let adjusted_staking_reward_amount = if (slashed_validator_indices_set.contains(&i)) { + // Use the slashing rate to compute the amount of staking rewards slashed from this punished validator. + // Use u128 to avoid multiplication overflow. + let staking_reward_adjustment_u128 = ((unadjusted_staking_reward_amount as u128) * (reward_slashing_rate as u128)) / BASIS_POINT_DENOMINATOR; + unadjusted_staking_reward_amount - (staking_reward_adjustment_u128 as u64) + } else { + // Otherwise, unadjusted staking reward amount is assigned to the unslashed validators + unadjusted_staking_reward_amount + }; + adjusted_staking_reward_amounts.push_back(adjusted_staking_reward_amount); - + + // Move to the next validator i = i + 1; }; + // The sum of the adjusted staking rewards may not be equal to the total staking reward, + // because of integer division truncation and the slashing of the rewards for the slashed validators. adjusted_staking_reward_amounts } diff --git a/crates/iota-framework/packages/iota-system/tests/rewards_distribution_tests.move b/crates/iota-framework/packages/iota-system/tests/rewards_distribution_tests.move index b8ad5e8b7f5..b57fb2e841e 100644 --- a/crates/iota-framework/packages/iota-system/tests/rewards_distribution_tests.move +++ b/crates/iota-framework/packages/iota-system/tests/rewards_distribution_tests.move @@ -47,22 +47,50 @@ module iota_system::rewards_distribution_tests { // need to advance epoch so validator's staking starts counting advance_epoch(scenario); + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + 100 * NANOS_PER_IOTA, + 200 * NANOS_PER_IOTA, + 300 * NANOS_PER_IOTA, + 400 * NANOS_PER_IOTA, + ], + scenario + ); + advance_epoch_with_reward_amounts(0, 100, scenario); + + // rewards of 100 IOTA are split evenly between the validators + // => +25 IOTA for each validator assert_validator_total_stake_amounts( validator_addrs(), - vector[125 * NANOS_PER_IOTA, 225 * NANOS_PER_IOTA, 325 * NANOS_PER_IOTA, 425 * NANOS_PER_IOTA], + vector[ + (100 + 25) * NANOS_PER_IOTA, + (200 + 25) * NANOS_PER_IOTA, + (300 + 25) * NANOS_PER_IOTA, + (400 + 25) * NANOS_PER_IOTA, + ], scenario ); stake_with(VALIDATOR_ADDR_2, VALIDATOR_ADDR_2, 720, scenario); advance_epoch(scenario); + advance_epoch_with_reward_amounts(0, 100, scenario); + // Even though validator 2 has a lot more stake now, it should not get more rewards because // the voting power is capped at 10%. + // rewards of 100 IOTA are split evenly between the validators + // => +25 IOTA for each validator assert_validator_total_stake_amounts( validator_addrs(), - vector[150 * NANOS_PER_IOTA, 970 * NANOS_PER_IOTA, 350 * NANOS_PER_IOTA, 450 * NANOS_PER_IOTA], + vector[ + (125 + 25) * NANOS_PER_IOTA, + (225 + 720 + 25) * NANOS_PER_IOTA, + (325 + 25) * NANOS_PER_IOTA, + (425 + 25) * NANOS_PER_IOTA, + ], scenario ); @@ -77,9 +105,31 @@ module iota_system::rewards_distribution_tests { // need to advance epoch so validator's staking starts counting advance_epoch(scenario); + + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + 100_000_000 * NANOS_PER_IOTA, + 200_000_000 * NANOS_PER_IOTA, + 300_000_000 * NANOS_PER_IOTA, + 400_000_000 * NANOS_PER_IOTA, + ], + scenario + ); advance_epoch_with_reward_amounts(0, 100, scenario); - assert_validator_total_stake_amounts(validator_addrs(), vector[100_000_025 * NANOS_PER_IOTA, 200_000_025 * NANOS_PER_IOTA, 300_000_025 * NANOS_PER_IOTA, 400_000_025 * NANOS_PER_IOTA], scenario); + + // rewards of 100 IOTA are split evenly between the validators + // => +25 IOTA for each validator + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + (100_000_000 + 25) * NANOS_PER_IOTA, + (200_000_000 + 25) * NANOS_PER_IOTA, + (300_000_000 + 25) * NANOS_PER_IOTA, + (400_000_000 + 25) * NANOS_PER_IOTA, + ], + scenario); scenario_val.end(); } @@ -98,6 +148,7 @@ module iota_system::rewards_distribution_tests { advance_epoch_with_target_reward_amounts(validator_target_reward, 0, computation_reward, scenario); let new_supply = total_supply(scenario); + // Since the target reward and computation reward are the same, no new tokens should // have been minted, so the supply should stay constant. assert!(prev_supply == new_supply, 0); @@ -120,6 +171,7 @@ module iota_system::rewards_distribution_tests { advance_epoch_with_target_reward_amounts(validator_target_reward, 0, computation_reward, scenario); let new_supply = total_supply(scenario); + // The difference between target reward and computation reward should have been burned. assert_eq(prev_supply - (computation_reward - validator_target_reward) * NANOS_PER_IOTA, new_supply); @@ -141,6 +193,7 @@ module iota_system::rewards_distribution_tests { advance_epoch_with_target_reward_amounts(validator_target_reward, 0, computation_reward, scenario); let new_supply = total_supply(scenario); + // The difference between target reward and computation reward should have been minted. assert_eq(prev_supply + (validator_target_reward - computation_reward) * NANOS_PER_IOTA, new_supply); @@ -153,27 +206,39 @@ module iota_system::rewards_distribution_tests { let mut scenario_val = test_scenario::begin(VALIDATOR_ADDR_1); let scenario = &mut scenario_val; + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); - // V1: 100, V2: 200, V3: 300, V4: 400 - advance_epoch_with_target_reward_amounts(800, 0, 400, scenario); + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + 100 * NANOS_PER_IOTA, + 200 * NANOS_PER_IOTA, + 300 * NANOS_PER_IOTA, + 400 * NANOS_PER_IOTA, + ], + scenario + ); // The computation reward is lower than the target reward, so 400 IOTA should be minted. - // Each validator pool has 25% of the voting power and thus gets 25% of the reward (200 IOTA). + advance_epoch_with_target_reward_amounts(800, 0, 400, scenario); + + // Each validator pool has 25% of the voting power and thus gets 25% of the reward. + // => +200 IOTA for each validator assert_validator_total_stake_amounts( validator_addrs(), vector[ - (100 + 200) * NANOS_PER_IOTA, - (200 + 200) * NANOS_PER_IOTA, - (300 + 200) * NANOS_PER_IOTA, - (400 + 200) * NANOS_PER_IOTA, + (100 + 200) * NANOS_PER_IOTA, + (200 + 200) * NANOS_PER_IOTA, + (300 + 200) * NANOS_PER_IOTA, + (400 + 200) * NANOS_PER_IOTA, ], scenario ); unstake(VALIDATOR_ADDR_1, 0, scenario); - // Validator should get the entire reward of 200 plus its initially staked 100 IOTA. + // Validator 1 should get the entire reward of 200 plus its initially staked 100 IOTA. assert_eq(total_iota_balance(VALIDATOR_ADDR_1, scenario), (100+200) * NANOS_PER_IOTA); scenario_val.end(); @@ -185,27 +250,39 @@ module iota_system::rewards_distribution_tests { let mut scenario_val = test_scenario::begin(VALIDATOR_ADDR_1); let scenario = &mut scenario_val; + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); - // V1: 100, V2: 200, V3: 300, V4: 400 - advance_epoch_with_target_reward_amounts(800, 0, 1000, scenario); + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + 100 * NANOS_PER_IOTA, + 200 * NANOS_PER_IOTA, + 300 * NANOS_PER_IOTA, + 400 * NANOS_PER_IOTA, + ], + scenario + ); // The computation reward is higher than the target reward, so 200 IOTA should be burned. - // Each validator pool has 25% of the voting power and thus gets 25% of the reward (200 IOTA). + advance_epoch_with_target_reward_amounts(800, 0, 1000, scenario); + + // Each validator pool has 25% of the voting power and thus gets 25% of the reward. + // => +200 IOTA for each validator assert_validator_total_stake_amounts( validator_addrs(), vector[ - (100 + 200) * NANOS_PER_IOTA, - (200 + 200) * NANOS_PER_IOTA, - (300 + 200) * NANOS_PER_IOTA, - (400 + 200) * NANOS_PER_IOTA, + (100 + 200) * NANOS_PER_IOTA, + (200 + 200) * NANOS_PER_IOTA, + (300 + 200) * NANOS_PER_IOTA, + (400 + 200) * NANOS_PER_IOTA, ], scenario ); unstake(VALIDATOR_ADDR_1, 0, scenario); - // Validator should get the entire reward of 200 plus its initially staked 100 IOTA. + // Validator 1 should get the entire reward of 200 plus its initially staked 100 IOTA. assert_eq(total_iota_balance(VALIDATOR_ADDR_1, scenario), (100+200) * NANOS_PER_IOTA); scenario_val.end(); @@ -219,15 +296,28 @@ module iota_system::rewards_distribution_tests { stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 100, scenario); stake_with(STAKER_ADDR_2, VALIDATOR_ADDR_2, 50, scenario); + + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); - // V1: 200, V2: 250, V3: 300, V4: 400 + + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + (100 + 100) * NANOS_PER_IOTA, + (200 + 50) * NANOS_PER_IOTA, + 300 * NANOS_PER_IOTA, + 400 * NANOS_PER_IOTA, + ], + scenario + ); set_commission_rate_and_advance_epoch(VALIDATOR_ADDR_1, 500, scenario); // 5% commission + // The computation reward is lower than the target reward, so 400 IOTA should be minted. advance_epoch_with_target_reward_amounts(800, 0, 400, scenario); - // The computation reward is lower than the target reward, so 400 IOTA should be minted. - // Each validator pool has 25% of the voting power and thus gets 25% of the reward (200 IOTA each). + // Each validator pool has 25% of the voting power and thus gets 25% of the reward. + // => +200 IOTA for each validator assert_validator_total_stake_amounts( validator_addrs(), vector[ @@ -260,32 +350,78 @@ module iota_system::rewards_distribution_tests { stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 200, scenario); stake_with(STAKER_ADDR_2, VALIDATOR_ADDR_2, 100, scenario); - advance_epoch(scenario); - assert_validator_total_stake_amounts(validator_addrs(), vector[300 * NANOS_PER_IOTA, 300 * NANOS_PER_IOTA, 300 * NANOS_PER_IOTA, 400 * NANOS_PER_IOTA], scenario); - assert_validator_self_stake_amounts(validator_addrs(), vector[100 * NANOS_PER_IOTA, 200 * NANOS_PER_IOTA, 300 * NANOS_PER_IOTA, 400 * NANOS_PER_IOTA], scenario); + // need to advance epoch so validator's staking starts counting + advance_epoch(scenario); - // Each pool gets 30 IOTA. + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + (100 + 200) * NANOS_PER_IOTA, + (200 + 100) * NANOS_PER_IOTA, + 300 * NANOS_PER_IOTA, + 400 * NANOS_PER_IOTA, + ], + scenario); + + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + 100 * NANOS_PER_IOTA, + 200 * NANOS_PER_IOTA, + 300 * NANOS_PER_IOTA, + 400 * NANOS_PER_IOTA, + ], scenario); + + // Each validator pool has 25% of the voting power and thus gets 25% of the reward. + // => +30 IOTA for each pool + // Validator 1 gets 100/300 * 30 IOTA => +10 IOTA for validator 1 + // Validator 2 gets 200/300 * 30 IOTA => +20 IOTA for validator 2 + // Validators 3 and 4 have all the stake in the pool => +30 IOTA for validators 3 and 4 advance_epoch_with_reward_amounts(0, 120, scenario); - assert_validator_self_stake_amounts(validator_addrs(), vector[110 * NANOS_PER_IOTA, 220 * NANOS_PER_IOTA, 330 * NANOS_PER_IOTA, 430 * NANOS_PER_IOTA], scenario); + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + (100 + 10) * NANOS_PER_IOTA, + (200 + 20) * NANOS_PER_IOTA, + (300 + 30) * NANOS_PER_IOTA, + (400 + 30) * NANOS_PER_IOTA, + ], + scenario); + unstake(STAKER_ADDR_1, 0, scenario); stake_with(STAKER_ADDR_2, VALIDATOR_ADDR_1, 600, scenario); - // Each pool gets 30 IOTA. + + // Each validator pool has 25% of the voting power and thus gets 25% of the reward. + // => +30 IOTA for each pool advance_epoch_with_reward_amounts(0, 120, scenario); - // staker 1 receives only 20 IOTA of rewards, not 40 since we are using pre-epoch exchange rate. - assert_eq(total_iota_balance(STAKER_ADDR_1, scenario), 220 * NANOS_PER_IOTA); - assert_validator_self_stake_amounts(validator_addrs(), vector[140 * NANOS_PER_IOTA, 240 * NANOS_PER_IOTA, 360 * NANOS_PER_IOTA, 460 * NANOS_PER_IOTA], scenario); + // staker 1 receives only 200/300*30=20 IOTA of rewards, (not 40) since we are using pre-epoch exchange rate. + assert_eq(total_iota_balance(STAKER_ADDR_1, scenario), (200 + 20) * NANOS_PER_IOTA); + // The recent changes in stake are not valid for this epoch yet. Thus: + // Validators 1, 3 and 4 have all the stake in the pool => +30 IOTA for validators 1, 3 and 4 + // Validator 2 gets 200/300 * 30 IOTA => +20 IOTA for validator + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + (110 + 30) * NANOS_PER_IOTA, + (220 + 20) * NANOS_PER_IOTA, + (330 + 30) * NANOS_PER_IOTA, + (430 + 30) * NANOS_PER_IOTA, + ], + scenario); + unstake(STAKER_ADDR_2, 0, scenario); - assert_eq(total_iota_balance(STAKER_ADDR_2, scenario), 120 * NANOS_PER_IOTA); // 20 IOTA of rewards received + // staker 2 receives its available principal (100 IOTA) + 100/300*60=20 IOTA of rewards, relative to 2 epochs. + // The stake added in the last epoch (600 IOTA) is not unstaked yet. + assert_eq(total_iota_balance(STAKER_ADDR_2, scenario), (100 + 20) * NANOS_PER_IOTA); + // +10 IOTA for each pool advance_epoch_with_reward_amounts(0, 40, scenario); - unstake(STAKER_ADDR_2, 0, scenario); // unstake 600 principal IOTA - // additional 600 IOTA of principal and 46 IOTA of rewards withdrawn to Coin<IOTA> - // For this stake, the staking exchange rate is 100 : 140 and the unstaking - // exchange rate is 528 : 750 -ish so the total iota withdraw will be: - // (600 * 100 / 140) * 750 / 528 = ~608. Together with the 120 IOTA we already have, - // that would be about 728 IOTA. + // unstakes the additional 600 IOTA of principal + rewards relative to past epoch + // the rewarded amount is 600/740*10 IOTA + // staker 2's balance is then ~120 + 600 + 8.1081081 + unstake(STAKER_ADDR_2, 0, scenario); // TODO: Come up with better numbers and clean it up! assert_eq(total_iota_balance(STAKER_ADDR_2, scenario), 728108108107); scenario_val.end(); @@ -300,6 +436,7 @@ module iota_system::rewards_distribution_tests { // stake a large amount stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 200000000, scenario); + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); advance_epoch_with_reward_amounts(0, 150000, scenario); @@ -324,28 +461,80 @@ module iota_system::rewards_distribution_tests { stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 100, scenario); stake_with(STAKER_ADDR_2, VALIDATOR_ADDR_2, 100, scenario); + + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); + // Pool's stake: // V1: 200, V2: 300, V3: 300, V4: 400 set_commission_rate_and_advance_epoch(VALIDATOR_ADDR_2, 2000, scenario); // 20% commission + + // Each validator pool has 25% of the voting power and thus gets 25% of the reward. + // => +30 IOTA for each pool advance_epoch_with_reward_amounts(0, 120, scenario); - // V1: 230, V2: 330, V3: 330, V4: 430 - // 2 IOTA, or 20 % of staker_2's rewards, goes to validator_2 - assert_validator_non_self_stake_amounts(validator_addrs(), vector[115 * NANOS_PER_IOTA, 108 * NANOS_PER_IOTA, 0, 0], scenario); - assert_validator_self_stake_amounts(validator_addrs(), vector[115 * NANOS_PER_IOTA, 222 * NANOS_PER_IOTA, 330 * NANOS_PER_IOTA, 430 * NANOS_PER_IOTA], scenario); + + // staker 1 gets 100/200*30 IOTA => +15 IOTA + // staker 2 would get 100/300*30 IOTA = 10 IOTA. However, since the commission rate is 20% for this pool, they get 8 IOTA + assert_validator_non_self_stake_amounts( + validator_addrs(), + vector[ + (100 + 15) * NANOS_PER_IOTA, + (100 + 8) * NANOS_PER_IOTA, + 0, + 0, + ], + scenario); + + // Validator 1 gets 100/200*30 => +15 IOTA + // Validator 2 gets 200/300*30 + 2 (from the commission) => +22 IOTA + // Validators 3 and 4 have all the pool stake and get +30 IOTA each. + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + (100 + 15) * NANOS_PER_IOTA, + (200 + 22) * NANOS_PER_IOTA, + (300 + 30) * NANOS_PER_IOTA, + (400 + 30) * NANOS_PER_IOTA, + ], + scenario); set_commission_rate_and_advance_epoch(VALIDATOR_ADDR_1, 1000, scenario); // 10% commission + // +60 IOTA for each pool advance_epoch_with_reward_amounts(0, 240, scenario); - assert_validator_total_stake_amounts(validator_addrs(), vector[290 * NANOS_PER_IOTA, 390 * NANOS_PER_IOTA, 390 * NANOS_PER_IOTA, 490 * NANOS_PER_IOTA], scenario); + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + (230 + 60) * NANOS_PER_IOTA, + (330 + 60) * NANOS_PER_IOTA, + (330 + 60) * NANOS_PER_IOTA, + (430 + 60) * NANOS_PER_IOTA, + ], scenario); // Staker 1 rewards in the recent distribution is 0.9 x 30 = 27 IOTA // Validator 1 rewards in the recent distribution is 60 - 27 = 33 IOTA // Staker 2 amounts for 0.8 * 60 * (108 / 330) + 108 = 123.709 IOTA // Validator 2 amounts for 390 - 123.709 = 266.291 IOTA - assert_validator_non_self_stake_amounts(validator_addrs(), vector[142 * NANOS_PER_IOTA, 123709090909, 0, 0], scenario); - assert_validator_self_stake_amounts(validator_addrs(), vector[148 * NANOS_PER_IOTA, 266290909091, 390 * NANOS_PER_IOTA, 490 * NANOS_PER_IOTA], scenario); + assert_validator_non_self_stake_amounts( + validator_addrs(), + vector[ + 142 * NANOS_PER_IOTA, + 123709090909, + 0, + 0, + ], + scenario); + + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + 148 * NANOS_PER_IOTA, + 266290909091, + 390 * NANOS_PER_IOTA, + 490 * NANOS_PER_IOTA, + ], + scenario); scenario_val.end(); } @@ -357,6 +546,8 @@ module iota_system::rewards_distribution_tests { let scenario = &mut scenario_val; stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 100, scenario); + + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); // V1: 200, V2: 200, V3: 300, V4: 400 @@ -397,7 +588,9 @@ module iota_system::rewards_distribution_tests { set_up_iota_system_state(); let mut scenario_val = test_scenario::begin(VALIDATOR_ADDR_1); let scenario = &mut scenario_val; + let initial_supply = total_supply(scenario); + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 100, scenario); @@ -405,7 +598,7 @@ module iota_system::rewards_distribution_tests { advance_epoch(scenario); - // validator_2 is reported by 3 other validators, so 75% of total stake. + // validator_2 is reported by 3 other validators, so 75% of total stake, since the voting power is capped at 10%. report_validator(VALIDATOR_ADDR_1, VALIDATOR_ADDR_2, scenario); report_validator(VALIDATOR_ADDR_3, VALIDATOR_ADDR_2, scenario); report_validator(VALIDATOR_ADDR_4, VALIDATOR_ADDR_2, scenario); @@ -415,24 +608,38 @@ module iota_system::rewards_distribution_tests { // 3600 IOTA of total rewards, 50% threshold and 10% reward slashing. // So validator_2 is the only one whose rewards should get slashed. + // Each pool would get +900 IOTA, disregarding the slashing rate + // Validator 1 gets 100/200*900 = +450 IOTA + // Validator 2 would get 200/300*900 = +600 IOTA (disregarding slashing) + // Validators 3 and 4 have all the pool stake, so they get +900 IOTA each advance_epoch_with_reward_amounts_and_slashing_rates( 0, 3600, 1000, scenario ); // Without reward slashing, the validator's stakes should be [100+450, 200+600, 300+900, 400+900] // after the last epoch advancement. - // Since 60 IOTA, or 10% of validator_2's rewards (600) are slashed, she only has 800 - 60 = 740 now. - // There are in total 90 IOTA of rewards slashed (60 from the validator, and 30 from her staker) - // so the unslashed validators each get their share of additional rewards, which is 30. - assert_validator_self_stake_amounts(validator_addrs(), vector[565 * NANOS_PER_IOTA, 740 * NANOS_PER_IOTA, 1230 * NANOS_PER_IOTA, 1330 * NANOS_PER_IOTA], scenario); + // Since 60 IOTA, or 10% of validator_2's rewards (600) are slashed, she only has 200 + 600 - 60 = 740 now. + // Note that the slashed rewards are not distributed to the other validators. + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + (100 + 450) * NANOS_PER_IOTA, + (200 + 600 - 60) * NANOS_PER_IOTA, + (300 + 900) * NANOS_PER_IOTA, + (400 + 900) * NANOS_PER_IOTA, + ], + scenario); // Unstake so we can check the stake rewards as well. unstake(STAKER_ADDR_1, 0, scenario); unstake(STAKER_ADDR_2, 0, scenario); - // Same analysis as above. Delegator 1 has 3 additional IOTA, and 10% of staker 2's rewards are slashed. - assert!(total_iota_balance(STAKER_ADDR_1, scenario) == 565 * NANOS_PER_IOTA); - assert!(total_iota_balance(STAKER_ADDR_2, scenario) == 370 * NANOS_PER_IOTA); + // Same analysis as above. Delegator 1 gets 450 IOTA, and 10% of staker 2's rewards (30 IOTA) are slashed. + assert!(total_iota_balance(STAKER_ADDR_1, scenario) == (100 + 450) * NANOS_PER_IOTA); + assert!(total_iota_balance(STAKER_ADDR_2, scenario) == (100 + 300 - 30) * NANOS_PER_IOTA); + + // Ensure that the slashed rewards are burned. + assert_eq(total_supply(scenario), initial_supply - 90 * NANOS_PER_IOTA); scenario_val.end(); } @@ -441,7 +648,9 @@ module iota_system::rewards_distribution_tests { set_up_iota_system_state(); let mut scenario_val = test_scenario::begin(VALIDATOR_ADDR_1); let scenario = &mut scenario_val; + let initial_supply = total_supply(scenario); + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 100, scenario); @@ -449,14 +658,17 @@ module iota_system::rewards_distribution_tests { advance_epoch(scenario); - // validator_2 is reported by 3 other validators, so 75% of total stake. + // validator_2 is reported by 3 other validators, so 75% of total stake, since the voting power is capped at 10%. report_validator(VALIDATOR_ADDR_1, VALIDATOR_ADDR_2, scenario); report_validator(VALIDATOR_ADDR_3, VALIDATOR_ADDR_2, scenario); report_validator(VALIDATOR_ADDR_4, VALIDATOR_ADDR_2, scenario); - // 3600 IOTA of total rewards, 100% reward slashing. // So validator_2 is the only one whose rewards should get slashed. + // Each pool would get +900 IOTA, disregarding the slashing rate + // Validator 1 gets 100/200*900 = +450 IOTA + // Validator 2 would get 200/300*900 = +600 IOTA (disregarding slashing) + // Validators 3 and 4 have all the pool stake, so they get +900 IOTA each advance_epoch_with_reward_amounts_and_slashing_rates( 0, 3600, 10_000, scenario ); @@ -464,16 +676,27 @@ module iota_system::rewards_distribution_tests { // Without reward slashing, the validator's stakes should be [100+450, 200+600, 300+900, 400+900] // after the last epoch advancement. // The entire rewards of validator 2's staking pool are slashed, which is 900 IOTA. - // so the unslashed validators each get their share of additional rewards, which is 300. - assert_validator_self_stake_amounts(validator_addrs(), vector[(550 + 150) * NANOS_PER_IOTA, 200 * NANOS_PER_IOTA, (1200 + 300) * NANOS_PER_IOTA, (1300 + 300) * NANOS_PER_IOTA], scenario); + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + (100 + 450) * NANOS_PER_IOTA, + (200 + 600 - 600) * NANOS_PER_IOTA, + (300 + 900) * NANOS_PER_IOTA, + (400 + 900) * NANOS_PER_IOTA, + ], + scenario); // Unstake so we can check the stake rewards as well. unstake(STAKER_ADDR_1, 0, scenario); unstake(STAKER_ADDR_2, 0, scenario); - // Same analysis as above. Staker 1 has 150 additional IOTA, and since all of staker 2's rewards are slashed she only gets back her principal. - assert!(total_iota_balance(STAKER_ADDR_1, scenario) == (550 + 150) * NANOS_PER_IOTA); - assert!(total_iota_balance(STAKER_ADDR_2, scenario) == 100 * NANOS_PER_IOTA); + // Same analysis as above. Staker 1 gets 450 IOTA as rewards, and since all of staker 2's rewards are slashed she only gets back her principal. + assert!(total_iota_balance(STAKER_ADDR_1, scenario) == (100 + 450) * NANOS_PER_IOTA); + assert!(total_iota_balance(STAKER_ADDR_2, scenario) == (100 + 300 - 300) * NANOS_PER_IOTA); + + // Ensure that the slashed rewards are burned. + assert_eq(total_supply(scenario), initial_supply - 900 * NANOS_PER_IOTA); + scenario_val.end(); } @@ -482,41 +705,64 @@ module iota_system::rewards_distribution_tests { set_up_iota_system_state(); let mut scenario_val = test_scenario::begin(VALIDATOR_ADDR_1); let scenario = &mut scenario_val; + let initial_supply = total_supply(scenario); - // Put 300 IOTA into the storage fund. + // Put 300 IOTA into the storage fund. This should not change the pools' stake or give rewards. advance_epoch_with_reward_amounts(300, 0, scenario); + assert_validator_total_stake_amounts( + validator_addrs(), + vector[ + 100 * NANOS_PER_IOTA, + 200 * NANOS_PER_IOTA, + 300 * NANOS_PER_IOTA, + 400 * NANOS_PER_IOTA, + ], scenario); // Add a few stakes. - stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_3, 100, scenario); + stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_3, 200, scenario); stake_with(STAKER_ADDR_2, VALIDATOR_ADDR_4, 100, scenario); + + // need to advance epoch so validator's staking starts counting advance_epoch(scenario); - // validator_4 is reported by 3 other validators, so 75% of total stake. + // validator_4 is reported by 3 other validators, so 75% of total stake, since the voting power is capped at 10%. report_validator(VALIDATOR_ADDR_1, VALIDATOR_ADDR_4, scenario); report_validator(VALIDATOR_ADDR_2, VALIDATOR_ADDR_4, scenario); report_validator(VALIDATOR_ADDR_3, VALIDATOR_ADDR_4, scenario); // 1000 IOTA of storage charges, 1500 IOTA of computation rewards, 50% slashing threshold // and 20% slashing rate + // because of the voting power cap, each pool gets +375 IOTA advance_epoch_with_reward_amounts_and_slashing_rates( 1000, 1500, 2000, scenario ); - // Each unslashed validator staking pool gets 375 IOTA of computation rewards + 25 IOTA (1/3) of validator 4's slashed computation reward, - // so in total it gets 400 IOTA of rewards. - // Validator 3's should get (375 + 25) * 3/4 = 300 in computation rewards. - // Validator 4's should get (375 - 75) * 4/5 = 240 in computation rewards. - assert_validator_self_stake_amounts(validator_addrs(), vector[500 * NANOS_PER_IOTA, 600 * NANOS_PER_IOTA, 600 * NANOS_PER_IOTA, 640 * NANOS_PER_IOTA], scenario); + // Validator 1 should get 375 * 1 = 375 in rewards. + // Validator 2 should get 375 * 1 = 375 in rewards. + // Validator 3 should get 375 * 3/5 = 225 in rewards. + // Validator 4 should get (375 - 75) * 4/5 = 240 in rewards. + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + (100 + 375) * NANOS_PER_IOTA, + (200 + 375) * NANOS_PER_IOTA, + (300 + 225) * NANOS_PER_IOTA, + (400 + 240) * NANOS_PER_IOTA, + ], + scenario); // Unstake so we can check the stake rewards as well. unstake(STAKER_ADDR_1, 0, scenario); unstake(STAKER_ADDR_2, 0, scenario); - // Staker 1 gets (375 + 25) * 1/4 = 100 IOTA of rewards. - assert_eq(total_iota_balance(STAKER_ADDR_1, scenario), (100 + 100) * NANOS_PER_IOTA); + // Staker 1 gets 375 * 2/5 = 150 IOTA of rewards. + assert_eq(total_iota_balance(STAKER_ADDR_1, scenario), (200 + 150) * NANOS_PER_IOTA); // Staker 2 gets (375 - 75) * 1/5 = 60 IOTA of rewards. assert_eq(total_iota_balance(STAKER_ADDR_2, scenario), (100 + 60) * NANOS_PER_IOTA); + // Ensure that the slashed rewards are burned. + assert_eq(total_supply(scenario), initial_supply - 75 * NANOS_PER_IOTA); + scenario_val.end(); } @@ -527,6 +773,7 @@ module iota_system::rewards_distribution_tests { set_up_iota_system_state(); let mut scenario_val = test_scenario::begin(VALIDATOR_ADDR_1); let scenario = &mut scenario_val; + let initial_supply = total_supply(scenario); slash_all_validators(scenario); @@ -535,7 +782,14 @@ module iota_system::rewards_distribution_tests { ); // All validators should have 0 rewards added so their stake stays the same. - assert_validator_self_stake_amounts(validator_addrs(), vector[100 * NANOS_PER_IOTA, 200 * NANOS_PER_IOTA, 300 * NANOS_PER_IOTA, 400 * NANOS_PER_IOTA], scenario); + assert_validator_self_stake_amounts( + validator_addrs(), + vector[ + 100 * NANOS_PER_IOTA, + 200 * NANOS_PER_IOTA, + 300 * NANOS_PER_IOTA, + 400 * NANOS_PER_IOTA, + ], scenario); scenario.next_tx(@0x0); // Storage fund balance should be the same as before. @@ -545,6 +799,9 @@ module iota_system::rewards_distribution_tests { // The entire 1000 IOTA of storage charges should go to the object rebate portion of the storage fund. assert_eq(system_state.get_storage_fund_object_rebates(), 1000 * NANOS_PER_IOTA); + // Ensure that the slashed rewards are burned. + assert_eq(system_state.get_total_iota_supply(), initial_supply - 500 * NANOS_PER_IOTA); + test_scenario::return_shared(system_state); scenario_val.end(); } @@ -557,28 +814,45 @@ module iota_system::rewards_distribution_tests { stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 220, scenario); + // since the voting power is capped at 10%, each pool gets +10 IOTA + // Pools' stake after this are + // P1: 100 + 220 + 10 = 330; P2: 200 + 10 = 210; P3: 300 + 10 = 310; P4: 400 + 10 = 410 advance_epoch_with_reward_amounts(0, 40, scenario); stake_with(STAKER_ADDR_2, VALIDATOR_ADDR_1, 480, scenario); - // Staker 1 gets 2/3 * 1/4 * 120 = 20 IOTA here. + // Here, each pool gets +30 IOTA + // Staker 1 gets 220/330 * 30 = +20 IOTA, totalling 240 IOTA of stake + // Pools' stake after this are + // P1: 330 + 480 + 30 = 840; P2: 210 + 30 = 240; P3: 310 + 30 = 340; P4: 410 + 30 = 440 advance_epoch_with_reward_amounts(0, 120, scenario); stake_with(STAKER_ADDR_1, VALIDATOR_ADDR_1, 130, scenario); stake_with(STAKER_ADDR_3, VALIDATOR_ADDR_1, 390, scenario); - // Staker 1 gets 20 IOTA here and staker 2 gets 40 IOTA here. + // Here, each pool gets +70 IOTA + // Staker 1 gets 240/840*70 = +20 IOTA, totalling 390 IOTA of stake + // Staker 2 gets 480/840*70 = +40 IOTA, totalling 520 IOTA of stake + // Pools' stake after this are + // P1: 840 + 130 + 390 + 70 = 1430; P2: 240 + 70 = 310; P3: 340 + 70 = 410; P4: 440 + 70 = 510 advance_epoch_with_reward_amounts(0, 280, scenario); + stake_with(STAKER_ADDR_3, VALIDATOR_ADDR_1, 280, scenario); stake_with(STAKER_ADDR_4, VALIDATOR_ADDR_1, 1400, scenario); - // Staker 1 gets 30 IOTA, staker 2 gets 40 IOTA and staker 3 gets 30 IOTA. + // Here, each pool gets +110 IOTA + // Staker 1 gets 390/1430*110 = +30 IOTA, totalling 420 IOTA of stake + // Staker 2 gets 520/1430*110 = +40 IOTA, totalling 560 IOTA of stake + // Staker 3 gets 390/1430*110 = +30 IOTA, totalling 700 IOTA of stake + // Pools' stake after this are + // P1: 1430 + 280 + 1400 + 110 = 3220; P2: 310 + 110 = 420 + // P3: 410 + 110 = 520; P4: 510 + 110 = 620 advance_epoch_with_reward_amounts(0, 440, scenario); scenario.next_tx(@0x0); let mut system_state = scenario.take_shared<IotaSystemState>(); // Check that we have the right amount of IOTA in the staking pool. - assert_eq(system_state.validator_stake_amount(VALIDATOR_ADDR_1), 140 * 23 * NANOS_PER_IOTA); + assert_eq(system_state.validator_stake_amount(VALIDATOR_ADDR_1), 3220 * NANOS_PER_IOTA); test_scenario::return_shared(system_state); // Withdraw all stakes at once. @@ -589,22 +863,16 @@ module iota_system::rewards_distribution_tests { unstake(STAKER_ADDR_3, 0, scenario); unstake(STAKER_ADDR_4, 0, scenario); - // staker 1's first stake was active for 3 epochs so got 20 * 3 = 60 IOTA of rewards - // and her second stake was active for only one epoch and got 10 IOTA of rewards. - assert_eq(total_iota_balance(STAKER_ADDR_1, scenario), (220 + 130 + 20 * 3 + 10) * NANOS_PER_IOTA); - // staker 2's stake was active for 2 epochs so got 40 * 2 = 80 IOTA of rewards - assert_eq(total_iota_balance(STAKER_ADDR_2, scenario), (480 + 40 * 2) * NANOS_PER_IOTA); - // staker 3's first stake was active for 1 epoch and got 30 IOTA of rewards - // and her second stake didn't get any rewards. - assert_eq(total_iota_balance(STAKER_ADDR_3, scenario), (390 + 280 + 30) * NANOS_PER_IOTA); - // staker 4 joined and left in an epoch where no rewards were earned so she got no rewards. + assert_eq(total_iota_balance(STAKER_ADDR_1, scenario), 420 * NANOS_PER_IOTA); + assert_eq(total_iota_balance(STAKER_ADDR_2, scenario), 560 * NANOS_PER_IOTA); + assert_eq(total_iota_balance(STAKER_ADDR_3, scenario), 700 * NANOS_PER_IOTA); assert_eq(total_iota_balance(STAKER_ADDR_4, scenario), 1400 * NANOS_PER_IOTA); advance_epoch_with_reward_amounts(0, 0, scenario); scenario.next_tx(@0x0); let mut system_state = scenario.take_shared<IotaSystemState>(); - // Since all the stakes are gone the pool is empty except for the validator's original stake. + // Since all the stakes are gone the pool is empty except for the validator's stake. assert_eq(system_state.validator_stake_amount(VALIDATOR_ADDR_1), 140 * NANOS_PER_IOTA); test_scenario::return_shared(system_state); scenario_val.end(); @@ -704,6 +972,11 @@ module iota_system::rewards_distribution_tests { scenario_val.end(); } + // This will set up the IOTA system state with the following validator stakes: + // Valdiator 1 => 100 + // Valdiator 2 => 200 + // Valdiator 3 => 300 + // Valdiator 4 => 400 fun set_up_iota_system_state() { let mut scenario_val = test_scenario::begin(@0x0); let scenario = &mut scenario_val; @@ -719,6 +992,11 @@ module iota_system::rewards_distribution_tests { scenario_val.end(); } + // This will set up the IOTA system state with the following validator stakes: + // Valdiator 1 => 100000000 + // Valdiator 2 => 200000000 + // Valdiator 3 => 300000000 + // Valdiator 4 => 400000000 fun set_up_iota_system_state_with_big_amounts() { let mut scenario_val = test_scenario::begin(@0x0); let scenario = &mut scenario_val; diff --git a/crates/iota-framework/packages_compiled/iota-system b/crates/iota-framework/packages_compiled/iota-system index 794306b9814..3869d79f95f 100644 Binary files a/crates/iota-framework/packages_compiled/iota-system and b/crates/iota-framework/packages_compiled/iota-system differ diff --git a/crates/iota-framework/published_api.txt b/crates/iota-framework/published_api.txt index fc186af1667..72299612773 100644 --- a/crates/iota-framework/published_api.txt +++ b/crates/iota-framework/published_api.txt @@ -508,7 +508,7 @@ find_validator find_validator_from_table_vec fun 0x3::validator_set -get_validator_indices +get_validator_indices_set fun 0x3::validator_set get_validator_mut @@ -565,9 +565,6 @@ calculate_total_stakes adjust_stake_and_gas_price fun 0x3::validator_set -compute_reward_adjustments - fun - 0x3::validator_set compute_slashed_validators fun 0x3::validator_set diff --git a/crates/iota-genesis-builder/Cargo.toml b/crates/iota-genesis-builder/Cargo.toml index 81d3c77ffe1..c62d44ebfbf 100644 --- a/crates/iota-genesis-builder/Cargo.toml +++ b/crates/iota-genesis-builder/Cargo.toml @@ -16,6 +16,7 @@ bcs.workspace = true bigdecimal = "0.4" camino.workspace = true clap.workspace = true +csv = "1.2" fastcrypto.workspace = true flate2.workspace = true fs_extra = "1.3" diff --git a/crates/iota-genesis-builder/src/main.rs b/crates/iota-genesis-builder/src/main.rs index ba782f7b042..403080b5ee7 100644 --- a/crates/iota-genesis-builder/src/main.rs +++ b/crates/iota-genesis-builder/src/main.rs @@ -13,7 +13,7 @@ use iota_genesis_builder::{ stardust::{ migration::{Migration, MigrationTargetNetwork}, parse::HornetSnapshotParser, - types::output_header::OutputHeader, + types::{address_swap_map::AddressSwapMap, output_header::OutputHeader}, }, }; use iota_sdk::types::block::{ @@ -42,6 +42,11 @@ enum Snapshot { Iota { #[clap(long, help = "Path to the Iota Hornet full-snapshot file")] snapshot_path: String, + #[clap( + long, + help = "Path to the address swap map file. This must be a CSV file with two columns, where an entry contains in the first column an IotaAddress present in the Hornet full-snapshot and in the second column an IotaAddress that will be used for the swap." + )] + address_swap_map_path: String, #[clap(long, value_parser = clap::value_parser!(MigrationTargetNetwork), help = "Target network for migration")] target_network: MigrationTargetNetwork, }, @@ -56,11 +61,17 @@ fn main() -> Result<()> { // Parse the CLI arguments let cli = Cli::parse(); - let (snapshot_path, target_network, coin_type) = match cli.snapshot { + let (snapshot_path, address_swap_map_path, target_network, coin_type) = match cli.snapshot { Snapshot::Iota { snapshot_path, + address_swap_map_path, target_network, - } => (snapshot_path, target_network, CoinType::Iota), + } => ( + snapshot_path, + address_swap_map_path, + target_network, + CoinType::Iota, + ), }; // Start the Hornet snapshot parser @@ -73,12 +84,14 @@ fn main() -> Result<()> { CoinType::Iota => scale_amount_for_iota(snapshot_parser.total_supply()?)?, }; + let address_swap_map = AddressSwapMap::from_csv(&address_swap_map_path)?; // Prepare the migration using the parser output stream let migration = Migration::new( snapshot_parser.target_milestone_timestamp(), total_supply, target_network, coin_type, + address_swap_map, )?; // Prepare the writer for the objects snapshot diff --git a/crates/iota-genesis-builder/src/stardust/migration/executor.rs b/crates/iota-genesis-builder/src/stardust/migration/executor.rs index 816123dba63..d11f85c8c00 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/executor.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/executor.rs @@ -35,7 +35,6 @@ use iota_types::{ stardust::{ coin_type::CoinType, output::{Nft, foundry::create_foundry_amount_coin}, - stardust_to_iota_address, stardust_to_iota_address_owner, }, timelock::timelock, transaction::{ @@ -53,7 +52,10 @@ use crate::{ MigrationTargetNetwork, PACKAGE_DEPS, create_migration_context, package_module_bytes, verification::created_objects::CreatedObjects, }, - types::{output_header::OutputHeader, token_scheme::SimpleTokenSchemeU64}, + types::{ + address_swap_map::AddressSwapMap, output_header::OutputHeader, + token_scheme::SimpleTokenSchemeU64, + }, }, }; @@ -307,6 +309,7 @@ impl Executor { header: &OutputHeader, alias: &AliasOutput, coin_type: CoinType, + address_swap_map: &mut AddressSwapMap, ) -> Result<CreatedObjects> { let mut created_objects = CreatedObjects::default(); @@ -316,7 +319,8 @@ impl Executor { let move_alias = iota_types::stardust::output::Alias::try_from_stardust(alias_id, alias)?; // TODO: We should ensure that no circular ownership exists. - let alias_output_owner = stardust_to_iota_address_owner(alias.governor_address())?; + let alias_output_owner = + address_swap_map.swap_stardust_to_iota_address_owner(alias.governor_address())?; let package_deps = InputObjects::new(self.load_packages(PACKAGE_DEPS).collect()); let version = package_deps.lamport_timestamp(&[]); @@ -558,10 +562,14 @@ impl Executor { basic_output: &BasicOutput, target_milestone_timestamp_sec: u32, coin_type: &CoinType, + address_swap_map: &mut AddressSwapMap, ) -> Result<CreatedObjects> { let mut basic = iota_types::stardust::output::BasicOutput::new(header.new_object_id(), basic_output)?; - let owner: IotaAddress = basic_output.address().to_string().parse()?; + + let basic_objects_owner = + address_swap_map.swap_stardust_to_iota_address(basic_output.address())?; + let mut created_objects = CreatedObjects::default(); // The minimum version of the manually created objects @@ -570,11 +578,12 @@ impl Executor { let object = if basic.is_simple_coin(target_milestone_timestamp_sec) { if !basic_output.native_tokens().is_empty() { - let coins = self.create_native_token_coins(basic_output.native_tokens(), owner)?; + let coins = self + .create_native_token_coins(basic_output.native_tokens(), basic_objects_owner)?; created_objects.set_native_tokens(coins)?; } let amount_coin = basic.into_genesis_coin_object( - owner, + basic_objects_owner, &self.protocol_config, &self.tx_context, version, @@ -596,7 +605,7 @@ impl Executor { basic.native_tokens.id = UID::new(self.tx_context.fresh_id()); } let object = basic.to_genesis_object( - owner, + basic_objects_owner, &self.protocol_config, &self.tx_context, version, @@ -618,10 +627,12 @@ impl Executor { output_id: OutputId, basic_output: &BasicOutput, target_milestone_timestamp: u32, + address_swap_map: &mut AddressSwapMap, ) -> Result<CreatedObjects> { let mut created_objects = CreatedObjects::default(); - let owner: IotaAddress = basic_output.address().to_string().parse()?; + let basic_output_owner = + address_swap_map.swap_stardust_to_iota_address(basic_output.address())?; let package_deps = InputObjects::new(self.load_packages(PACKAGE_DEPS).collect()); let version = package_deps.lamport_timestamp(&[]); @@ -631,7 +642,7 @@ impl Executor { let object = timelock::to_genesis_object( timelock, - owner, + basic_output_owner, &self.protocol_config, &self.tx_context, version, @@ -648,6 +659,7 @@ impl Executor { header: &OutputHeader, nft: &NftOutput, coin_type: CoinType, + address_swap_map: &mut AddressSwapMap, ) -> Result<CreatedObjects> { let mut created_objects = CreatedObjects::default(); @@ -657,8 +669,11 @@ impl Executor { let move_nft = Nft::try_from_stardust(nft_id, nft)?; // TODO: We should ensure that no circular ownership exists. - let nft_output_owner_address = stardust_to_iota_address(nft.address())?; - let nft_output_owner = stardust_to_iota_address_owner(nft.address())?; + let nft_output_owner_address = + address_swap_map.swap_stardust_to_iota_address(nft.address())?; + + let nft_output_owner = + address_swap_map.swap_stardust_to_iota_address_owner(nft.address())?; let package_deps = InputObjects::new(self.load_packages(PACKAGE_DEPS).collect()); let version = package_deps.lamport_timestamp(&[]); diff --git a/crates/iota-genesis-builder/src/stardust/migration/migration.rs b/crates/iota-genesis-builder/src/stardust/migration/migration.rs index c07efda78ec..96ef8c7fd2a 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/migration.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/migration.rs @@ -32,7 +32,7 @@ use crate::stardust::{ verification::{created_objects::CreatedObjects, verify_outputs}, }, native_token::package_data::NativeTokenPackageData, - types::output_header::OutputHeader, + types::{address_swap_map::AddressSwapMap, output_header::OutputHeader}, }; /// We fix the protocol version used in the migration. @@ -74,6 +74,7 @@ pub struct Migration { /// The coin type to use in order to migrate outputs. Can only be equal to /// `Iota` at the moment. Is fixed for the entire migration process. coin_type: CoinType, + address_swap_map: AddressSwapMap, } impl Migration { @@ -84,6 +85,7 @@ impl Migration { total_supply: u64, target_network: MigrationTargetNetwork, coin_type: CoinType, + address_swap_map: AddressSwapMap, ) -> Result<Self> { let executor = Executor::new( ProtocolVersion::new(MIGRATION_PROTOCOL_VERSION), @@ -96,6 +98,7 @@ impl Migration { executor, output_objects_map: Default::default(), coin_type, + address_swap_map, }) } @@ -135,7 +138,7 @@ impl Migration { .collect::<Vec<_>>(); info!("Verifying ledger state..."); self.verify_ledger_state(&outputs)?; - + self.address_swap_map.verify_all_addresses_swapped()?; Ok(()) } @@ -194,14 +197,18 @@ impl Migration { ) -> Result<()> { for (header, output) in outputs { let created = match output { - Output::Alias(alias) => { - self.executor - .create_alias_objects(header, alias, self.coin_type)? - } - Output::Nft(nft) => { - self.executor - .create_nft_objects(header, nft, self.coin_type)? - } + Output::Alias(alias) => self.executor.create_alias_objects( + header, + alias, + self.coin_type, + &mut self.address_swap_map, + )?, + Output::Nft(nft) => self.executor.create_nft_objects( + header, + nft, + self.coin_type, + &mut self.address_swap_map, + )?, Output::Basic(basic) => { // All timelocked vested rewards(basic outputs with the specific ID format) // should be migrated as TimeLock<Balance<IOTA>> objects. @@ -214,6 +221,7 @@ impl Migration { header.output_id(), basic, self.target_milestone_timestamp_sec, + &mut self.address_swap_map, )? } else { self.executor.create_basic_objects( @@ -221,6 +229,7 @@ impl Migration { basic, self.target_milestone_timestamp_sec, &self.coin_type, + &mut self.address_swap_map, )? } } @@ -244,6 +253,7 @@ impl Migration { self.target_milestone_timestamp_sec, self.total_supply, self.executor.store(), + &self.address_swap_map, )?; Ok(()) } diff --git a/crates/iota-genesis-builder/src/stardust/migration/tests/basic.rs b/crates/iota-genesis-builder/src/stardust/migration/tests/basic.rs index 3888ef58d1e..b503be9862c 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/tests/basic.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/tests/basic.rs @@ -32,7 +32,7 @@ use crate::stardust::{ random_output_header, unlock_object, }, }, - types::output_header::OutputHeader, + types::{address_swap_map::AddressSwapMap, output_header::OutputHeader}, }; /// Test the id of a `BasicOutput` that is transformed to a simple coin. @@ -53,6 +53,7 @@ fn basic_simple_coin_id() { 1_000_000, MigrationTargetNetwork::Mainnet, CoinType::Iota, + AddressSwapMap::default(), ) .unwrap(); migration @@ -103,6 +104,7 @@ fn basic_simple_coin_id_with_expired_timelock() { 1_000_000, MigrationTargetNetwork::Mainnet, CoinType::Iota, + AddressSwapMap::default(), ) .unwrap(); migration @@ -139,6 +141,7 @@ fn basic_id() { 1_000_000, MigrationTargetNetwork::Mainnet, CoinType::Iota, + AddressSwapMap::default(), ) .unwrap(); migration @@ -183,6 +186,7 @@ fn basic_simple_coin_migration_with_native_token() { 1_000_000, MigrationTargetNetwork::Mainnet, CoinType::Iota, + AddressSwapMap::default(), ) .unwrap(); migration.run_migration(outputs).unwrap(); @@ -225,6 +229,7 @@ fn basic_simple_coin_migration_with_native_tokens() { 1_000_000, MigrationTargetNetwork::Mainnet, CoinType::Iota, + AddressSwapMap::default(), ) .unwrap(); migration.run_migration(outputs.clone()).unwrap(); diff --git a/crates/iota-genesis-builder/src/stardust/migration/tests/mod.rs b/crates/iota-genesis-builder/src/stardust/migration/tests/mod.rs index b55599cdb5a..f694f1b8758 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/tests/mod.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/tests/mod.rs @@ -39,7 +39,10 @@ use crate::stardust::{ }, verification::created_objects::CreatedObjects, }, - types::{output_header::OutputHeader, output_index::random_output_index}, + types::{ + address_swap_map::AddressSwapMap, output_header::OutputHeader, + output_index::random_output_index, + }, }; mod alias; @@ -63,8 +66,13 @@ fn run_migration( outputs: impl IntoIterator<Item = (OutputHeader, Output)>, coin_type: CoinType, ) -> anyhow::Result<(Executor, HashMap<OutputId, CreatedObjects>)> { - let mut migration = - Migration::new(1, total_supply, MigrationTargetNetwork::Mainnet, coin_type)?; + let mut migration = Migration::new( + 1, + total_supply, + MigrationTargetNetwork::Mainnet, + coin_type, + AddressSwapMap::default(), + )?; migration.run_migration(outputs)?; Ok(migration.into_parts()) } diff --git a/crates/iota-genesis-builder/src/stardust/migration/verification/alias.rs b/crates/iota-genesis-builder/src/stardust/migration/verification/alias.rs index f8cf0e1b23a..bc75037c894 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/verification/alias.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/verification/alias.rs @@ -17,15 +17,18 @@ use iota_types::{ }, }; -use crate::stardust::migration::{ - executor::FoundryLedgerData, - verification::{ - created_objects::CreatedObjects, - util::{ - verify_address_owner, verify_issuer_feature, verify_metadata_feature, - verify_native_tokens, verify_parent, verify_sender_feature, +use crate::stardust::{ + migration::{ + executor::FoundryLedgerData, + verification::{ + created_objects::CreatedObjects, + util::{ + verify_address_owner, verify_issuer_feature, verify_metadata_feature, + verify_native_tokens, verify_parent, verify_sender_feature, + }, }, }, + types::address_swap_map::AddressSwapMap, }; pub(super) fn verify_alias_output( @@ -35,6 +38,7 @@ pub(super) fn verify_alias_output( foundry_data: &HashMap<stardust::TokenId, FoundryLedgerData>, storage: &InMemoryStorage, total_value: &mut u64, + address_swap_map: &AddressSwapMap, ) -> anyhow::Result<()> { let alias_id = ObjectID::new(*output.alias_id_non_null(&output_id)); @@ -53,6 +57,7 @@ pub(super) fn verify_alias_output( output.governor_address(), created_output_obj, "alias output", + address_swap_map, )?; // Alias Owner diff --git a/crates/iota-genesis-builder/src/stardust/migration/verification/basic.rs b/crates/iota-genesis-builder/src/stardust/migration/verification/basic.rs index 8748efba4a3..ddd9efb6889 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/verification/basic.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/verification/basic.rs @@ -18,17 +18,20 @@ use iota_types::{ }, }; -use crate::stardust::migration::{ - executor::FoundryLedgerData, - verification::{ - created_objects::CreatedObjects, - util::{ - verify_address_owner, verify_coin, verify_expiration_unlock_condition, - verify_metadata_feature, verify_native_tokens, verify_parent, verify_sender_feature, - verify_storage_deposit_unlock_condition, verify_tag_feature, - verify_timelock_unlock_condition, +use crate::stardust::{ + migration::{ + executor::FoundryLedgerData, + verification::{ + created_objects::CreatedObjects, + util::{ + verify_address_owner, verify_coin, verify_expiration_unlock_condition, + verify_metadata_feature, verify_native_tokens, verify_parent, + verify_sender_feature, verify_storage_deposit_unlock_condition, verify_tag_feature, + verify_timelock_unlock_condition, + }, }, }, + types::address_swap_map::AddressSwapMap, }; pub(super) fn verify_basic_output( @@ -39,6 +42,7 @@ pub(super) fn verify_basic_output( target_milestone_timestamp: u32, storage: &InMemoryStorage, total_value: &mut u64, + address_swap_map: &AddressSwapMap, ) -> Result<()> { // If this is a timelocked vested reward, a `Timelock<Balance>` is created. if is_timelocked_vested_reward(output_id, output, target_milestone_timestamp) { @@ -120,7 +124,12 @@ pub(super) fn verify_basic_output( created_output_obj.owner, ); } else { - verify_address_owner(output.address(), created_output_obj, "basic output")?; + verify_address_owner( + output.address(), + created_output_obj, + "basic output", + address_swap_map, + )?; } // Amount @@ -191,7 +200,7 @@ pub(super) fn verify_basic_output( .as_coin_maybe() .ok_or_else(|| anyhow!("expected a coin"))?; - verify_address_owner(output.address(), created_coin_obj, "coin")?; + verify_address_owner(output.address(), created_coin_obj, "coin", address_swap_map)?; verify_coin(output.amount(), &created_coin)?; *total_value += created_coin.value(); diff --git a/crates/iota-genesis-builder/src/stardust/migration/verification/foundry.rs b/crates/iota-genesis-builder/src/stardust/migration/verification/foundry.rs index da43737ee98..68aed45cfe3 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/verification/foundry.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/verification/foundry.rs @@ -23,7 +23,7 @@ use crate::stardust::{ }, }, native_token::package_data::NativeTokenPackageData, - types::token_scheme::SimpleTokenSchemeU64, + types::{address_swap_map::AddressSwapMap, token_scheme::SimpleTokenSchemeU64}, }; pub(super) fn verify_foundry_output( @@ -33,6 +33,7 @@ pub(super) fn verify_foundry_output( foundry_data: &HashMap<TokenId, FoundryLedgerData>, storage: &InMemoryStorage, total_value: &mut u64, + address_swap_map: &AddressSwapMap, ) -> Result<()> { let foundry_data = foundry_data .get(&output.token_id()) @@ -54,7 +55,7 @@ pub(super) fn verify_foundry_output( .as_coin_maybe() .ok_or_else(|| anyhow!("expected a coin"))?; - verify_address_owner(alias_address, created_coin_obj, "coin")?; + verify_address_owner(alias_address, created_coin_obj, "coin", address_swap_map)?; verify_coin(output.amount(), &created_coin)?; *total_value += created_coin.value(); @@ -239,6 +240,7 @@ pub(super) fn verify_foundry_output( alias_address, coin_manager_treasury_cap_obj, "coin manager treasury cap", + address_swap_map, )?; verify_parent(&output_id, alias_address, storage)?; diff --git a/crates/iota-genesis-builder/src/stardust/migration/verification/mod.rs b/crates/iota-genesis-builder/src/stardust/migration/verification/mod.rs index b2d506680f6..983b19edc96 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/verification/mod.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/verification/mod.rs @@ -11,7 +11,10 @@ use iota_sdk::types::block::output::{Output, OutputId, TokenId}; use iota_types::in_memory_storage::InMemoryStorage; use self::created_objects::CreatedObjects; -use crate::stardust::{migration::executor::FoundryLedgerData, types::output_header::OutputHeader}; +use crate::stardust::{ + migration::executor::FoundryLedgerData, + types::{address_swap_map::AddressSwapMap, output_header::OutputHeader}, +}; pub mod alias; pub mod basic; @@ -27,6 +30,7 @@ pub(crate) fn verify_outputs<'a>( target_milestone_timestamp: u32, total_supply: u64, storage: &InMemoryStorage, + address_swap_map: &AddressSwapMap, ) -> anyhow::Result<()> { let mut total_value = 0; for (header, output) in outputs { @@ -41,6 +45,7 @@ pub(crate) fn verify_outputs<'a>( target_milestone_timestamp, storage, &mut total_value, + address_swap_map, )?; } ensure!( @@ -58,6 +63,7 @@ fn verify_output( target_milestone_timestamp: u32, storage: &InMemoryStorage, total_value: &mut u64, + address_swap_map: &AddressSwapMap, ) -> anyhow::Result<()> { match output { Output::Alias(output) => alias::verify_alias_output( @@ -67,6 +73,7 @@ fn verify_output( foundry_data, storage, total_value, + address_swap_map, ), Output::Basic(output) => basic::verify_basic_output( header.output_id(), @@ -76,6 +83,7 @@ fn verify_output( target_milestone_timestamp, storage, total_value, + address_swap_map, ), Output::Foundry(output) => foundry::verify_foundry_output( header.output_id(), @@ -84,6 +92,7 @@ fn verify_output( foundry_data, storage, total_value, + address_swap_map, ), Output::Nft(output) => nft::verify_nft_output( header.output_id(), @@ -92,6 +101,7 @@ fn verify_output( foundry_data, storage, total_value, + address_swap_map, ), // Treasury outputs aren't used since Stardust, so no need to verify anything here. Output::Treasury(_) => return Ok(()), diff --git a/crates/iota-genesis-builder/src/stardust/migration/verification/nft.rs b/crates/iota-genesis-builder/src/stardust/migration/verification/nft.rs index 04ac251af6d..2d2bdc6fccb 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/verification/nft.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/verification/nft.rs @@ -15,17 +15,20 @@ use iota_types::{ stardust::output::{NFT_DYNAMIC_OBJECT_FIELD_KEY, NFT_DYNAMIC_OBJECT_FIELD_KEY_TYPE}, }; -use crate::stardust::migration::{ - executor::FoundryLedgerData, - verification::{ - created_objects::CreatedObjects, - util::{ - verify_address_owner, verify_expiration_unlock_condition, verify_issuer_feature, - verify_metadata_feature, verify_native_tokens, verify_parent, verify_sender_feature, - verify_storage_deposit_unlock_condition, verify_tag_feature, - verify_timelock_unlock_condition, +use crate::stardust::{ + migration::{ + executor::FoundryLedgerData, + verification::{ + created_objects::CreatedObjects, + util::{ + verify_address_owner, verify_expiration_unlock_condition, verify_issuer_feature, + verify_metadata_feature, verify_native_tokens, verify_parent, + verify_sender_feature, verify_storage_deposit_unlock_condition, verify_tag_feature, + verify_timelock_unlock_condition, + }, }, }, + types::address_swap_map::AddressSwapMap, }; pub(super) fn verify_nft_output( @@ -35,6 +38,7 @@ pub(super) fn verify_nft_output( foundry_data: &HashMap<TokenId, FoundryLedgerData>, storage: &InMemoryStorage, total_value: &mut u64, + address_swap_map: &AddressSwapMap, ) -> anyhow::Result<()> { let created_output_obj = created_objects.output().and_then(|id| { storage @@ -61,7 +65,12 @@ pub(super) fn verify_nft_output( created_output_obj.owner, ); } else { - verify_address_owner(output.address(), created_output_obj, "nft output")?; + verify_address_owner( + output.address(), + created_output_obj, + "nft output", + address_swap_map, + )?; } // NFT Owner diff --git a/crates/iota-genesis-builder/src/stardust/migration/verification/util.rs b/crates/iota-genesis-builder/src/stardust/migration/verification/util.rs index ea1672f6942..5c62e3fc8e7 100644 --- a/crates/iota-genesis-builder/src/stardust/migration/verification/util.rs +++ b/crates/iota-genesis-builder/src/stardust/migration/verification/util.rs @@ -22,13 +22,14 @@ use iota_types::{ object::{Object, Owner}, stardust::{ output::{Alias, Nft, unlock_conditions}, - stardust_to_iota_address, stardust_to_iota_address_owner, + stardust_to_iota_address, }, }; use tracing::warn; use crate::stardust::{ - migration::executor::FoundryLedgerData, types::token_scheme::MAX_ALLOWED_U64_SUPPLY, + migration::executor::FoundryLedgerData, + types::{address_swap_map::AddressSwapMap, token_scheme::MAX_ALLOWED_U64_SUPPLY}, }; pub(super) fn verify_native_tokens<NtKind: NativeTokenKind>( @@ -279,8 +280,10 @@ pub(super) fn verify_address_owner( owning_address: &Address, obj: &Object, name: &str, + address_swap_map: &AddressSwapMap, ) -> Result<()> { - let expected_owner = stardust_to_iota_address_owner(owning_address)?; + let expected_owner = address_swap_map.stardust_to_iota_address_owner(owning_address)?; + ensure!( obj.owner == expected_owner, "{name} owner mismatch: found {}, expected {}", diff --git a/crates/iota-genesis-builder/src/stardust/types/address_swap_map.rs b/crates/iota-genesis-builder/src/stardust/types/address_swap_map.rs new file mode 100644 index 00000000000..7dae6fb6e00 --- /dev/null +++ b/crates/iota-genesis-builder/src/stardust/types/address_swap_map.rs @@ -0,0 +1,255 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::HashMap; + +use iota_sdk::types::block::address::Address as StardustAddress; +use iota_types::{base_types::IotaAddress, object::Owner, stardust::stardust_to_iota_address}; + +type OriginAddress = IotaAddress; + +#[derive(Debug)] +pub struct DestinationAddress { + address: IotaAddress, + swapped: bool, +} + +impl DestinationAddress { + fn new(address: IotaAddress) -> Self { + Self { + address, + swapped: false, + } + } + + fn address(&self) -> IotaAddress { + self.address + } + + fn is_swapped(&self) -> bool { + self.swapped + } + + fn set_swapped(&mut self) { + self.swapped = true; + } +} + +#[derive(Debug, Default)] +pub struct AddressSwapMap { + addresses: HashMap<OriginAddress, DestinationAddress>, +} + +impl AddressSwapMap { + /// Retrieves the destination address for a given origin address. + pub fn destination_address(&self, origin_address: &OriginAddress) -> Option<IotaAddress> { + self.addresses + .get(origin_address) + .map(DestinationAddress::address) + } + + /// Retrieves the destination address for a given origin address. + /// Marks the origin address as swapped if found. + fn swap_destination_address(&mut self, origin_address: &OriginAddress) -> Option<IotaAddress> { + self.addresses.get_mut(origin_address).map(|destination| { + // Mark the origin address as swapped + destination.set_swapped(); + destination.address() + }) + } + + /// Verifies that all addresses have been swapped at least once. + /// Returns an error if any address is not swapped. + pub fn verify_all_addresses_swapped(&self) -> anyhow::Result<()> { + let unswapped_addresses = self + .addresses + .values() + .filter_map(|a| (!a.is_swapped()).then_some(a.address())) + .collect::<Vec<_>>(); + if !unswapped_addresses.is_empty() { + anyhow::bail!("unswapped addresses: {:?}", unswapped_addresses); + } + + Ok(()) + } + + /// Converts a [`StardustAddress`] to an [`Owner`] by first + /// converting it to an [`IotaAddress`] and then checking against the + /// swap map for potential address substitutions. + /// + /// If the address exists in the swap map, it is swapped with the + /// mapped destination address before being wrapped into an [`Owner`]. + pub fn stardust_to_iota_address_owner( + &self, + stardust_address: impl Into<StardustAddress>, + ) -> anyhow::Result<Owner> { + let mut address = stardust_to_iota_address(stardust_address)?; + if let Some(addr) = self.destination_address(&address) { + address = addr; + } + Ok(Owner::AddressOwner(address)) + } + + /// Converts a [`StardustAddress`] to an [`Owner`] by first + /// converting it to an [`IotaAddress`] and then checking against the + /// swap map for potential address substitutions. + /// + /// If the address exists in the swap map, it is swapped with the + /// mapped destination address before being wrapped into an [`Owner`]. + pub fn swap_stardust_to_iota_address_owner( + &mut self, + stardust_address: impl Into<StardustAddress>, + ) -> anyhow::Result<Owner> { + let mut address = stardust_to_iota_address(stardust_address)?; + if let Some(addr) = self.swap_destination_address(&address) { + address = addr; + } + Ok(Owner::AddressOwner(address)) + } + + /// Converts a [`StardustAddress`] to an [`IotaAddress`] and + /// checks against the swap map for potential address + /// substitutions. + /// + /// If the address exists in the swap map, it is swapped with the + /// mapped destination address before being returned as an + /// [`IotaAddress`]. + pub fn swap_stardust_to_iota_address( + &mut self, + stardust_address: impl Into<StardustAddress>, + ) -> anyhow::Result<IotaAddress> { + let mut address: IotaAddress = stardust_to_iota_address(stardust_address)?; + if let Some(addr) = self.swap_destination_address(&address) { + address = addr; + } + Ok(address) + } + + /// Initializes an [`AddressSwapMap`] by reading address pairs from a CSV + /// file. + /// + /// The function expects the file to contain two columns: the origin address + /// (first column) and the destination address (second column). These are + /// parsed into a [`HashMap`] that maps origin addresses to tuples + /// containing the destination address and a flag initialized to + /// `false`. + /// + /// # Example CSV File + /// ```csv + /// Origin,Destination + /// iota1qp8h9augeh6tk3uvlxqfapuwv93atv63eqkpru029p6sgvr49eufyz7katr,0xa12b4d6ec3f9a28437d5c8f3e96ba72d3c4e8f5ac98d17b1a3b8e9f2c71d4a3c + /// iota1qp7h2lkjhs6tk3uvlxqfjhlfw34atv63eqkpru356p6sgvr76eufyz1opkh,0x42d8c182eb1f3b2366d353eed4eb02a31d1d7982c0fd44683811d7036be3a85e + /// ``` + /// + /// # Parameters + /// - `file_path`: The relative path to the CSV file containing the address + /// mappings. + /// + /// # Returns + /// - An [`AddressSwapMap`] containing the parsed mappings. + /// + /// # Errors + /// - Returns an error if the file cannot be found, read, or parsed + /// correctly. + /// - Returns an error if the origin or destination addresses cannot be + /// parsed into an [`IotaAddress`]. + pub fn from_csv(file_path: &str) -> Result<AddressSwapMap, anyhow::Error> { + let current_dir = std::env::current_dir()?; + let file_path = current_dir.join(file_path); + let mut reader = csv::ReaderBuilder::new().from_path(file_path)?; + let mut addresses = HashMap::new(); + + verify_headers(reader.headers()?)?; + + for result in reader.records() { + let record = result?; + let origin: OriginAddress = + stardust_to_iota_address(StardustAddress::try_from_bech32(&record[0])?)?; + let destination: DestinationAddress = DestinationAddress::new(record[1].parse()?); + addresses.insert(origin, destination); + } + + Ok(AddressSwapMap { addresses }) + } + + #[cfg(test)] + pub fn addresses(&self) -> &HashMap<OriginAddress, DestinationAddress> { + &self.addresses + } +} + +fn verify_headers(headers: &csv::StringRecord) -> Result<(), anyhow::Error> { + const LEFT_HEADER: &str = "Origin"; + const RIGHT_HEADER: &str = "Destination"; + + if &headers[0] != LEFT_HEADER && &headers[1] != RIGHT_HEADER { + anyhow::bail!("Invalid CSV headers"); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::io::Write; + + use tempfile::NamedTempFile; + + use crate::stardust::types::address_swap_map::AddressSwapMap; + + fn write_temp_file(content: &str) -> NamedTempFile { + let mut file = NamedTempFile::new().unwrap(); + writeln!(file, "{}", content).unwrap(); + file + } + + #[test] + fn test_from_csv_valid_file() { + let content = "Origin,Destination\n\ + iota1qp8h9augeh6tk3uvlxqfapuwv93atv63eqkpru029p6sgvr49eufyz7katr,0xa12b4d6ec3f9a28437d5c8f3e96ba72d3c4e8f5ac98d17b1a3b8e9f2c71d4a3c"; + let file = write_temp_file(content); + let file_path = file.path().to_str().unwrap(); + let result = AddressSwapMap::from_csv(file_path); + assert!(result.is_ok()); + let map = result.unwrap(); + assert_eq!(map.addresses().len(), 1); + } + + #[test] + fn test_from_csv_missing_file() { + let result = AddressSwapMap::from_csv("nonexistent_file.csv"); + assert!(result.is_err()); + } + + #[test] + fn test_from_csv_invalid_headers() { + let content = "wrong_header1,wrong_header2\n\ + iota1qp8h9augeh6tk3uvlxqfapuwv93atv63eqkpru029p6sgvr49eufyz7katr,0xa12b4d6ec3f9a28437d5c8f3e96ba72d3c4e8f5ac98d17b1a3b8e9f2c71d4a3c"; + let file = write_temp_file(content); + let file_path = file.path().to_str().unwrap(); + let result = AddressSwapMap::from_csv(file_path); + assert!(result.is_err()); + } + + #[test] + fn test_from_csv_invalid_record() { + let content = "Origin,Destination\n\ + iota1qp8h9augeh6tk3uvlxqfapuwv93atv63eqkpru029p6sgvr49eufyz7katr,0xa12b4d6ec3f9a28437d5c8f3e96ba72d3c4e8f5ac98d17b1a3b8e9f2c71d4a3c,invalid_number"; + let file = write_temp_file(content); + let file_path = file.path().to_str().unwrap(); + let result = AddressSwapMap::from_csv(file_path); + + assert!(result.is_err()); + } + + #[test] + fn test_from_csv_empty_file() { + let content = "Origin,Destination"; + let file = write_temp_file(content); + let file_path = file.path().to_str().unwrap(); + let result = AddressSwapMap::from_csv(file_path); + assert!(result.is_ok()); + let map = result.unwrap(); + + assert_eq!(map.addresses().len(), 0); + } +} diff --git a/crates/iota-genesis-builder/src/stardust/types/mod.rs b/crates/iota-genesis-builder/src/stardust/types/mod.rs index cf2a0681889..49ea9368cf5 100644 --- a/crates/iota-genesis-builder/src/stardust/types/mod.rs +++ b/crates/iota-genesis-builder/src/stardust/types/mod.rs @@ -1,6 +1,7 @@ // Copyright (c) 2024 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +pub mod address_swap_map; pub mod output_header; pub mod output_index; pub mod snapshot; diff --git a/crates/iota-graphql-rpc/src/data/pg.rs b/crates/iota-graphql-rpc/src/data/pg.rs index 84abceb5677..9e321ede79b 100644 --- a/crates/iota-graphql-rpc/src/data/pg.rs +++ b/crates/iota-graphql-rpc/src/data/pg.rs @@ -97,7 +97,7 @@ impl QueryExecutor for PgExecutor { } } -impl<'c> super::DbConnection for PgConnection<'c> { +impl super::DbConnection for PgConnection<'_> { type Connection = diesel::PgConnection; type Backend = Pg; diff --git a/crates/iota-indexer/src/handlers/tx_processor.rs b/crates/iota-indexer/src/handlers/tx_processor.rs index 2ac77bbdb06..4ab9f640402 100644 --- a/crates/iota-indexer/src/handlers/tx_processor.rs +++ b/crates/iota-indexer/src/handlers/tx_processor.rs @@ -298,7 +298,7 @@ impl<'a> EpochEndIndexingObjectStore<'a> { } } -impl<'a> iota_types::storage::ObjectStore for EpochEndIndexingObjectStore<'a> { +impl iota_types::storage::ObjectStore for EpochEndIndexingObjectStore<'_> { fn get_object( &self, object_id: &ObjectID, diff --git a/crates/iota-indexer/tests/rpc-tests/governance_api.rs b/crates/iota-indexer/tests/rpc-tests/governance_api.rs index 3f662fef1d3..f79f35b607a 100644 --- a/crates/iota-indexer/tests/rpc-tests/governance_api.rs +++ b/crates/iota-indexer/tests/rpc-tests/governance_api.rs @@ -5,6 +5,7 @@ use iota_json_rpc_api::{GovernanceReadApiClient, TransactionBuilderClient}; use iota_json_rpc_types::{ DelegatedStake, DelegatedTimelockedStake, StakeStatus, TransactionBlockBytes, }; +use iota_protocol_config::ProtocolVersion; use iota_test_transaction_builder::TestTransactionBuilder; use iota_types::{ IOTA_FRAMEWORK_ADDRESS, IOTA_SYSTEM_ADDRESS, @@ -484,7 +485,7 @@ fn get_latest_iota_system_state() { indexer_wait_for_checkpoint(store, 1).await; let system_state = client.get_latest_iota_system_state().await.unwrap(); - assert_eq!(system_state.protocol_version, 1); + assert_eq!(system_state.protocol_version, ProtocolVersion::MAX.as_u64()); assert_eq!(system_state.system_state_version, 1); }); } diff --git a/crates/iota-indexer/tests/rpc-tests/read_api.rs b/crates/iota-indexer/tests/rpc-tests/read_api.rs index 382c6ea4ad3..94f0aa0f47c 100644 --- a/crates/iota-indexer/tests/rpc-tests/read_api.rs +++ b/crates/iota-indexer/tests/rpc-tests/read_api.rs @@ -8,6 +8,7 @@ use iota_json_rpc_types::{ CheckpointId, IotaGetPastObjectRequest, IotaObjectDataOptions, IotaObjectResponse, IotaObjectResponseQuery, IotaTransactionBlockResponse, IotaTransactionBlockResponseOptions, }; +use iota_protocol_config::ProtocolVersion; use iota_types::{ base_types::{ObjectID, SequenceNumber}, digests::TransactionDigest, @@ -1099,7 +1100,10 @@ fn get_protocol_config() { assert_eq!(fullnode_protocol_config, indexer_protocol_config); - let indexer_protocol_config = client.get_protocol_config(Some(1u64.into())).await.unwrap(); + let indexer_protocol_config = client + .get_protocol_config(Some(ProtocolVersion::MAX.as_u64().into())) + .await + .unwrap(); assert_eq!(fullnode_protocol_config, indexer_protocol_config); }); @@ -1122,7 +1126,11 @@ fn get_protocol_config_invalid_protocol_version() { assert!(rpc_call_error_msg_matches( result, - r#"{"code":-32603,"message":"Unsupported protocol version requested. Min supported: 1, max supported: 1"}"#, + &format!( + r#"{{"code":-32603,"message":"Unsupported protocol version requested. Min supported: {}, max supported: {}"}}"#, + ProtocolVersion::MIN.as_u64(), + ProtocolVersion::MAX.as_u64() + ), )); }); } diff --git a/crates/iota-json-rpc-types/src/displays/transaction_displays.rs b/crates/iota-json-rpc-types/src/displays/transaction_displays.rs index 767cd646219..330649f9339 100644 --- a/crates/iota-json-rpc-types/src/displays/transaction_displays.rs +++ b/crates/iota-json-rpc-types/src/displays/transaction_displays.rs @@ -15,7 +15,7 @@ use crate::{ IotaProgrammableTransactionBlock, displays::Pretty, }; -impl<'a> Display for Pretty<'a, IotaProgrammableTransactionBlock> { +impl Display for Pretty<'_, IotaProgrammableTransactionBlock> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(ptb) = self; let IotaProgrammableTransactionBlock { inputs, commands } = ptb; @@ -82,7 +82,7 @@ impl<'a> Display for Pretty<'a, IotaProgrammableTransactionBlock> { } } -impl<'a> Display for Pretty<'a, IotaCommand> { +impl Display for Pretty<'_, IotaCommand> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(command) = self; match command { @@ -141,7 +141,7 @@ impl<'a> Display for Pretty<'a, IotaCommand> { } } -impl<'a> Display for Pretty<'a, IotaProgrammableMoveCall> { +impl Display for Pretty<'_, IotaProgrammableMoveCall> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(move_call) = self; let IotaProgrammableMoveCall { @@ -170,7 +170,7 @@ impl<'a> Display for Pretty<'a, IotaProgrammableMoveCall> { } } -impl<'a> Display for Pretty<'a, IotaArgument> { +impl Display for Pretty<'_, IotaArgument> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(argument) = self; diff --git a/crates/iota-metrics/src/guards.rs b/crates/iota-metrics/src/guards.rs index f174b806da9..86b7881833f 100644 --- a/crates/iota-metrics/src/guards.rs +++ b/crates/iota-metrics/src/guards.rs @@ -22,7 +22,7 @@ impl<'a> GaugeGuard<'a> { } } -impl<'a> Drop for GaugeGuard<'a> { +impl Drop for GaugeGuard<'_> { /// Decrements the value of the `IntGauge` when the `IntGaugeGuard` is /// dropped. fn drop(&mut self) { @@ -56,7 +56,7 @@ pub struct GaugeGuardFuture<'a, F: Sized> { _guard: GaugeGuard<'a>, } -impl<'a, F: Future> Future for GaugeGuardFuture<'a, F> { +impl<F: Future> Future for GaugeGuardFuture<'_, F> { type Output = F::Output; /// Polls the wrapped future (`f`) to determine its readiness. This function diff --git a/crates/iota-metrics/src/histogram.rs b/crates/iota-metrics/src/histogram.rs index d33bf688cc0..5a7269f2015 100644 --- a/crates/iota-metrics/src/histogram.rs +++ b/crates/iota-metrics/src/histogram.rs @@ -387,7 +387,7 @@ impl HistogramReporter { } } -impl<'a> Drop for HistogramTimerGuard<'a> { +impl Drop for HistogramTimerGuard<'_> { /// Reports the elapsed time in milliseconds to the associated histogram /// when the `HistogramTimerGuard` is dropped. fn drop(&mut self) { diff --git a/crates/iota-metrics/src/metered_channel.rs b/crates/iota-metrics/src/metered_channel.rs index 210580e1b4b..14c86ec2313 100644 --- a/crates/iota-metrics/src/metered_channel.rs +++ b/crates/iota-metrics/src/metered_channel.rs @@ -192,7 +192,7 @@ impl<'a, T> Permit<'a, T> { } } -impl<'a, T> Drop for Permit<'a, T> { +impl<T> Drop for Permit<'_, T> { /// Custom drop logic for the `Permit` to handle cases where the permit is /// dropped without sending a value. This ensures that the occupancy of /// the channel is correctly updated by decrementing the associated gauge diff --git a/crates/iota-metrics/src/monitored_mpsc.rs b/crates/iota-metrics/src/monitored_mpsc.rs index 8c8ce92c931..ce15ff7fed3 100644 --- a/crates/iota-metrics/src/monitored_mpsc.rs +++ b/crates/iota-metrics/src/monitored_mpsc.rs @@ -188,7 +188,7 @@ impl<'a, T> Permit<'a, T> { } } -impl<'a, T> Drop for Permit<'a, T> { +impl<T> Drop for Permit<'_, T> { fn drop(&mut self) { // In the case the permit is dropped without sending, we still want to decrease // the occupancy of the channel. Otherwise, receiver should be diff --git a/crates/iota-open-rpc/spec/openrpc.json b/crates/iota-open-rpc/spec/openrpc.json index dfcd4ecccc2..f5ca3c7c01e 100644 --- a/crates/iota-open-rpc/spec/openrpc.json +++ b/crates/iota-open-rpc/spec/openrpc.json @@ -1300,7 +1300,7 @@ "name": "Result", "value": { "minSupportedProtocolVersion": "1", - "maxSupportedProtocolVersion": "1", + "maxSupportedProtocolVersion": "2", "protocolVersion": "1", "featureFlags": { "accept_zklogin_in_multisig": false, diff --git a/crates/iota-package-resolver/src/lib.rs b/crates/iota-package-resolver/src/lib.rs index 1fad3258d85..05ed900b686 100644 --- a/crates/iota-package-resolver/src/lib.rs +++ b/crates/iota-package-resolver/src/lib.rs @@ -1145,7 +1145,7 @@ impl OpenSignatureBody { } } -impl<'m, 'n> DatatypeRef<'m, 'n> { +impl DatatypeRef<'_, '_> { pub fn as_key(&self) -> DatatypeKey { DatatypeKey { package: self.package, diff --git a/crates/iota-protocol-config/src/lib.rs b/crates/iota-protocol-config/src/lib.rs index fe3b91a845e..a216a4efefb 100644 --- a/crates/iota-protocol-config/src/lib.rs +++ b/crates/iota-protocol-config/src/lib.rs @@ -16,11 +16,13 @@ use tracing::{info, warn}; /// The minimum and maximum protocol versions supported by this build. const MIN_PROTOCOL_VERSION: u64 = 1; -pub const MAX_PROTOCOL_VERSION: u64 = 1; +pub const MAX_PROTOCOL_VERSION: u64 = 2; // Record history of protocol version allocations here: // // Version 1: Original version. +// Version 2: Don't redistribute slashed staking rewards, fix computation of +// SystemEpochInfoEventV1. #[derive(Copy, Clone, Debug, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ProtocolVersion(u64); @@ -37,10 +39,10 @@ impl ProtocolVersion { #[cfg(not(msim))] const MAX_ALLOWED: Self = Self::MAX; - // We create 4 additional "fake" versions in simulator builds so that we can + // We create 3 additional "fake" versions in simulator builds so that we can // test upgrades. #[cfg(msim)] - pub const MAX_ALLOWED: Self = Self(MAX_PROTOCOL_VERSION + 4); + pub const MAX_ALLOWED: Self = Self(MAX_PROTOCOL_VERSION + 3); pub fn new(v: u64) -> Self { Self(v) @@ -898,9 +900,10 @@ pub struct ProtocolConfig { /// version. random_beacon_dkg_version: Option<u64>, - /// The maximum serialised transaction size (in bytes) accepted by - /// consensus. That should be bigger than the `max_tx_size_bytes` with - /// some additional headroom. + /// The maximum serialized transaction size (in bytes) accepted by + /// consensus. `consensus_max_transaction_size_bytes` should include + /// space for additional metadata, on top of the `max_tx_size_bytes` + /// value. consensus_max_transaction_size_bytes: Option<u64>, /// The maximum size of transactions included in a consensus block. consensus_max_transactions_in_block_bytes: Option<u64>, @@ -1159,8 +1162,8 @@ impl ProtocolConfig { #[cfg(msim)] { // populate the fake simulator version # with a different base tx cost. - if version == ProtocolVersion::MAX_ALLOWED { - let mut config = Self::get_for_version_impl(version - 1, Chain::Unknown); + if version > ProtocolVersion::MAX { + let mut config = Self::get_for_version_impl(ProtocolVersion::MAX, Chain::Unknown); config.base_tx_cost_fixed = Some(config.base_tx_cost_fixed() + 1000); return config; } @@ -1631,15 +1634,11 @@ impl ProtocolConfig { cfg.feature_flags.passkey_auth = true; } - // Ignore this check for the fake versions for - // `test_choose_next_system_packages`. TODO: remove the never_loop - // attribute when the version 2 is added. - #[allow(clippy::never_loop)] - #[cfg(not(msim))] for cur in 2..=version.0 { match cur { 1 => unreachable!(), - + // version 2 is a new framework version but with no config changes + 2 => {} // Use this template when making changes: // // // modify an existing constant. diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_2.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_2.snap new file mode 100644 index 00000000000..3f0064e48a4 --- /dev/null +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Mainnet_version_2.snap @@ -0,0 +1,270 @@ +--- +source: crates/iota-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +snapshot_kind: text +--- +version: 2 +feature_flags: + consensus_transaction_ordering: ByGasPrice + per_object_congestion_control_mode: TotalTxCount + zklogin_max_epoch_upper_bound_delta: 30 +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +gas_model_version: 1 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 10000 +reward_slashing_rate: 10000 +storage_gas_price: 76 +validator_target_reward: 767000000000000 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 1 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 1000 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: false +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 10 diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_2.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_2.snap new file mode 100644 index 00000000000..89cf049133a --- /dev/null +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__Testnet_version_2.snap @@ -0,0 +1,270 @@ +--- +source: crates/iota-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +snapshot_kind: text +--- +version: 2 +feature_flags: + consensus_transaction_ordering: ByGasPrice + per_object_congestion_control_mode: TotalTxCount + zklogin_max_epoch_upper_bound_delta: 30 +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +gas_model_version: 1 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 10000 +reward_slashing_rate: 10000 +storage_gas_price: 76 +validator_target_reward: 767000000000000 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 1 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 1000 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 10 diff --git a/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_2.snap b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_2.snap new file mode 100644 index 00000000000..e23a99f7062 --- /dev/null +++ b/crates/iota-protocol-config/src/snapshots/iota_protocol_config__test__version_2.snap @@ -0,0 +1,278 @@ +--- +source: crates/iota-protocol-config/src/lib.rs +expression: "ProtocolConfig::get_for_version(cur, *chain_id)" +snapshot_kind: text +--- +version: 2 +feature_flags: + consensus_transaction_ordering: ByGasPrice + enable_poseidon: true + enable_group_ops_native_function_msm: true + per_object_congestion_control_mode: TotalTxCount + zklogin_max_epoch_upper_bound_delta: 30 + enable_vdf: true + passkey_auth: true +max_tx_size_bytes: 131072 +max_input_objects: 2048 +max_size_written_objects: 5000000 +max_size_written_objects_system_tx: 50000000 +max_serialized_tx_effects_size_bytes: 524288 +max_serialized_tx_effects_size_bytes_system_tx: 8388608 +max_gas_payment_objects: 256 +max_modules_in_publish: 64 +max_package_dependencies: 32 +max_arguments: 512 +max_type_arguments: 16 +max_type_argument_depth: 16 +max_pure_argument_size: 16384 +max_programmable_tx_commands: 1024 +move_binary_format_version: 7 +min_move_binary_format_version: 6 +binary_module_handles: 100 +binary_struct_handles: 300 +binary_function_handles: 1500 +binary_function_instantiations: 750 +binary_signatures: 1000 +binary_constant_pool: 4000 +binary_identifiers: 10000 +binary_address_identifiers: 100 +binary_struct_defs: 200 +binary_struct_def_instantiations: 100 +binary_function_defs: 1000 +binary_field_handles: 500 +binary_field_instantiations: 250 +binary_friend_decls: 100 +max_move_object_size: 256000 +max_move_package_size: 102400 +max_publish_or_upgrade_per_ptb: 5 +max_tx_gas: 50000000000 +max_gas_price: 100000 +max_gas_computation_bucket: 5000000 +gas_rounding_step: 1000 +max_loop_depth: 5 +max_generic_instantiation_length: 32 +max_function_parameters: 128 +max_basic_blocks: 1024 +max_value_stack_size: 1024 +max_type_nodes: 256 +max_push_size: 10000 +max_struct_definitions: 200 +max_function_definitions: 1000 +max_fields_in_struct: 32 +max_dependency_depth: 100 +max_num_event_emit: 1024 +max_num_new_move_object_ids: 2048 +max_num_new_move_object_ids_system_tx: 32768 +max_num_deleted_move_object_ids: 2048 +max_num_deleted_move_object_ids_system_tx: 32768 +max_num_transferred_move_object_ids: 2048 +max_num_transferred_move_object_ids_system_tx: 32768 +max_event_emit_size: 256000 +max_event_emit_size_total: 65536000 +max_move_vector_len: 262144 +max_move_identifier_len: 128 +max_move_value_depth: 128 +max_back_edges_per_function: 10000 +max_back_edges_per_module: 10000 +max_verifier_meter_ticks_per_function: 16000000 +max_meter_ticks_per_module: 16000000 +max_meter_ticks_per_package: 16000000 +object_runtime_max_num_cached_objects: 1000 +object_runtime_max_num_cached_objects_system_tx: 16000 +object_runtime_max_num_store_entries: 1000 +object_runtime_max_num_store_entries_system_tx: 16000 +base_tx_cost_fixed: 1000 +package_publish_cost_fixed: 1000 +base_tx_cost_per_byte: 0 +package_publish_cost_per_byte: 80 +obj_access_cost_read_per_byte: 15 +obj_access_cost_mutate_per_byte: 40 +obj_access_cost_delete_per_byte: 40 +obj_access_cost_verify_per_byte: 200 +gas_model_version: 1 +obj_data_cost_refundable: 100 +obj_metadata_cost_non_refundable: 50 +storage_rebate_rate: 10000 +reward_slashing_rate: 10000 +storage_gas_price: 76 +validator_target_reward: 767000000000000 +max_transactions_per_checkpoint: 10000 +max_checkpoint_size_bytes: 31457280 +buffer_stake_for_protocol_upgrade_bps: 5000 +address_from_bytes_cost_base: 52 +address_to_u256_cost_base: 52 +address_from_u256_cost_base: 52 +config_read_setting_impl_cost_base: 100 +config_read_setting_impl_cost_per_byte: 40 +dynamic_field_hash_type_and_key_cost_base: 100 +dynamic_field_hash_type_and_key_type_cost_per_byte: 2 +dynamic_field_hash_type_and_key_value_cost_per_byte: 2 +dynamic_field_hash_type_and_key_type_tag_cost_per_byte: 2 +dynamic_field_add_child_object_cost_base: 100 +dynamic_field_add_child_object_type_cost_per_byte: 10 +dynamic_field_add_child_object_value_cost_per_byte: 10 +dynamic_field_add_child_object_struct_tag_cost_per_byte: 10 +dynamic_field_borrow_child_object_cost_base: 100 +dynamic_field_borrow_child_object_child_ref_cost_per_byte: 10 +dynamic_field_borrow_child_object_type_cost_per_byte: 10 +dynamic_field_remove_child_object_cost_base: 100 +dynamic_field_remove_child_object_child_cost_per_byte: 2 +dynamic_field_remove_child_object_type_cost_per_byte: 2 +dynamic_field_has_child_object_cost_base: 100 +dynamic_field_has_child_object_with_ty_cost_base: 100 +dynamic_field_has_child_object_with_ty_type_cost_per_byte: 2 +dynamic_field_has_child_object_with_ty_type_tag_cost_per_byte: 2 +event_emit_cost_base: 52 +event_emit_value_size_derivation_cost_per_byte: 2 +event_emit_tag_size_derivation_cost_per_byte: 5 +event_emit_output_cost_per_byte: 10 +object_borrow_uid_cost_base: 52 +object_delete_impl_cost_base: 52 +object_record_new_uid_cost_base: 52 +transfer_transfer_internal_cost_base: 52 +transfer_freeze_object_cost_base: 52 +transfer_share_object_cost_base: 52 +transfer_receive_object_cost_base: 52 +tx_context_derive_id_cost_base: 52 +types_is_one_time_witness_cost_base: 52 +types_is_one_time_witness_type_tag_cost_per_byte: 2 +types_is_one_time_witness_type_cost_per_byte: 2 +validator_validate_metadata_cost_base: 52 +validator_validate_metadata_data_cost_per_byte: 2 +crypto_invalid_arguments_cost: 100 +bls12381_bls12381_min_sig_verify_cost_base: 52 +bls12381_bls12381_min_sig_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_sig_verify_msg_cost_per_block: 2 +bls12381_bls12381_min_pk_verify_cost_base: 52 +bls12381_bls12381_min_pk_verify_msg_cost_per_byte: 2 +bls12381_bls12381_min_pk_verify_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_keccak256_cost_base: 52 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_k1_ecrecover_sha256_cost_base: 52 +ecdsa_k1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_k1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_k1_decompress_pubkey_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_cost_base: 52 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_k1_secp256k1_verify_sha256_cost_base: 52 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_k1_secp256k1_verify_sha256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_keccak256_cost_base: 52 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_keccak256_msg_cost_per_block: 2 +ecdsa_r1_ecrecover_sha256_cost_base: 52 +ecdsa_r1_ecrecover_sha256_msg_cost_per_byte: 2 +ecdsa_r1_ecrecover_sha256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_keccak256_cost_base: 52 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_keccak256_msg_cost_per_block: 2 +ecdsa_r1_secp256r1_verify_sha256_cost_base: 52 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_byte: 2 +ecdsa_r1_secp256r1_verify_sha256_msg_cost_per_block: 2 +ecvrf_ecvrf_verify_cost_base: 52 +ecvrf_ecvrf_verify_alpha_string_cost_per_byte: 2 +ecvrf_ecvrf_verify_alpha_string_cost_per_block: 2 +ed25519_ed25519_verify_cost_base: 52 +ed25519_ed25519_verify_msg_cost_per_byte: 2 +ed25519_ed25519_verify_msg_cost_per_block: 2 +groth16_prepare_verifying_key_bls12381_cost_base: 52 +groth16_prepare_verifying_key_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_base: 52 +groth16_verify_groth16_proof_internal_bls12381_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_bn254_cost_base: 52 +groth16_verify_groth16_proof_internal_bn254_cost_per_public_input: 2 +groth16_verify_groth16_proof_internal_public_input_cost_per_byte: 2 +hash_blake2b256_cost_base: 52 +hash_blake2b256_data_cost_per_byte: 2 +hash_blake2b256_data_cost_per_block: 2 +hash_keccak256_cost_base: 52 +hash_keccak256_data_cost_per_byte: 2 +hash_keccak256_data_cost_per_block: 2 +poseidon_bn254_cost_base: 260 +poseidon_bn254_cost_per_block: 10 +group_ops_bls12381_decode_scalar_cost: 52 +group_ops_bls12381_decode_g1_cost: 52 +group_ops_bls12381_decode_g2_cost: 52 +group_ops_bls12381_decode_gt_cost: 52 +group_ops_bls12381_scalar_add_cost: 52 +group_ops_bls12381_g1_add_cost: 52 +group_ops_bls12381_g2_add_cost: 52 +group_ops_bls12381_gt_add_cost: 52 +group_ops_bls12381_scalar_sub_cost: 52 +group_ops_bls12381_g1_sub_cost: 52 +group_ops_bls12381_g2_sub_cost: 52 +group_ops_bls12381_gt_sub_cost: 52 +group_ops_bls12381_scalar_mul_cost: 52 +group_ops_bls12381_g1_mul_cost: 52 +group_ops_bls12381_g2_mul_cost: 52 +group_ops_bls12381_gt_mul_cost: 52 +group_ops_bls12381_scalar_div_cost: 52 +group_ops_bls12381_g1_div_cost: 52 +group_ops_bls12381_g2_div_cost: 52 +group_ops_bls12381_gt_div_cost: 52 +group_ops_bls12381_g1_hash_to_base_cost: 52 +group_ops_bls12381_g2_hash_to_base_cost: 52 +group_ops_bls12381_g1_hash_to_cost_per_byte: 2 +group_ops_bls12381_g2_hash_to_cost_per_byte: 2 +group_ops_bls12381_g1_msm_base_cost: 52 +group_ops_bls12381_g2_msm_base_cost: 52 +group_ops_bls12381_g1_msm_base_cost_per_input: 52 +group_ops_bls12381_g2_msm_base_cost_per_input: 52 +group_ops_bls12381_msm_max_len: 32 +group_ops_bls12381_pairing_cost: 52 +hmac_hmac_sha3_256_cost_base: 52 +hmac_hmac_sha3_256_input_cost_per_byte: 2 +hmac_hmac_sha3_256_input_cost_per_block: 2 +check_zklogin_id_cost_base: 200 +check_zklogin_issuer_cost_base: 200 +vdf_verify_vdf_cost: 1500 +vdf_hash_to_input_cost: 100 +bcs_per_byte_serialized_cost: 2 +bcs_legacy_min_output_size_cost: 1 +bcs_failure_cost: 52 +hash_sha2_256_base_cost: 52 +hash_sha2_256_per_byte_cost: 2 +hash_sha2_256_legacy_min_input_len_cost: 1 +hash_sha3_256_base_cost: 52 +hash_sha3_256_per_byte_cost: 2 +hash_sha3_256_legacy_min_input_len_cost: 1 +type_name_get_base_cost: 52 +type_name_get_per_byte_cost: 2 +string_check_utf8_base_cost: 52 +string_check_utf8_per_byte_cost: 2 +string_is_char_boundary_base_cost: 52 +string_sub_string_base_cost: 52 +string_sub_string_per_byte_cost: 2 +string_index_of_base_cost: 52 +string_index_of_per_byte_pattern_cost: 2 +string_index_of_per_byte_searched_cost: 2 +vector_empty_base_cost: 52 +vector_length_base_cost: 52 +vector_push_back_base_cost: 52 +vector_push_back_legacy_per_abstract_memory_unit_cost: 2 +vector_borrow_base_cost: 52 +vector_pop_back_base_cost: 52 +vector_destroy_empty_base_cost: 52 +vector_swap_base_cost: 52 +debug_print_base_cost: 52 +debug_print_stack_trace_base_cost: 52 +execution_version: 1 +consensus_bad_nodes_stake_threshold: 20 +max_jwk_votes_per_validator_per_epoch: 240 +max_age_of_jwk_in_epochs: 1 +random_beacon_reduction_allowed_delta: 800 +random_beacon_reduction_lower_bound: 1000 +random_beacon_dkg_timeout_round: 3000 +random_beacon_min_round_interval_ms: 500 +random_beacon_dkg_version: 1 +consensus_max_transaction_size_bytes: 262144 +consensus_max_transactions_in_block_bytes: 524288 +consensus_max_num_transactions_in_block: 512 +max_deferral_rounds_for_congestion_control: 10 +min_checkpoint_interval_ms: 200 +checkpoint_summary_version_specific_data: 1 +max_soft_bundle_size: 5 +bridge_should_try_to_finalize_committee: true +max_accumulated_txn_cost_per_object_in_mysticeti_commit: 10 diff --git a/crates/iota-replay/src/config.rs b/crates/iota-replay/src/config.rs index 09ec3e4e264..8186b3c2f2b 100644 --- a/crates/iota-replay/src/config.rs +++ b/crates/iota-replay/src/config.rs @@ -106,7 +106,7 @@ impl Default for ReplayableNetworkConfigSet { name: "testnet".to_string(), epoch_zero_start_timestamp: 0, epoch_zero_rgp: 0, - public_full_node: url_from_str("https://fullnode.testnet.iota.io:443") + public_full_node: url_from_str("https://api.testnet.iota.cafe") .expect("invalid socket address") .to_string(), }; @@ -114,7 +114,7 @@ impl Default for ReplayableNetworkConfigSet { name: "devnet".to_string(), epoch_zero_start_timestamp: 0, epoch_zero_rgp: 0, - public_full_node: url_from_str("https://fullnode.devnet.iota.io:443") + public_full_node: url_from_str("https://api.devnet.iota.cafe") .expect("invalid socket address") .to_string(), }; diff --git a/crates/iota-replay/src/displays/gas_status_displays.rs b/crates/iota-replay/src/displays/gas_status_displays.rs index 0237a95449c..11858d59db7 100644 --- a/crates/iota-replay/src/displays/gas_status_displays.rs +++ b/crates/iota-replay/src/displays/gas_status_displays.rs @@ -12,7 +12,7 @@ use tabled::{ use crate::displays::Pretty; -impl<'a> Display for Pretty<'a, IotaGasStatus> { +impl Display for Pretty<'_, IotaGasStatus> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(iota_gas_status) = self; match iota_gas_status { diff --git a/crates/iota-replay/src/displays/transaction_displays.rs b/crates/iota-replay/src/displays/transaction_displays.rs index e13be0243aa..e7673957092 100644 --- a/crates/iota-replay/src/displays/transaction_displays.rs +++ b/crates/iota-replay/src/displays/transaction_displays.rs @@ -40,7 +40,7 @@ pub struct ResolvedResults { /// These Display implementations provide alternate displays that are used to /// format info contained in these Structs when calling the CLI replay command /// with an additional provided flag. -impl<'a> Display for Pretty<'a, FullPTB> { +impl Display for Pretty<'_, FullPTB> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(full_ptb) = self; let FullPTB { ptb, results } = full_ptb; @@ -146,7 +146,7 @@ impl<'a> Display for Pretty<'a, FullPTB> { } } -impl<'a> Display for Pretty<'a, Command> { +impl Display for Pretty<'_, Command> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(command) = self; match command { @@ -201,7 +201,7 @@ impl<'a> Display for Pretty<'a, Command> { } } -impl<'a> Display for Pretty<'a, ProgrammableMoveCall> { +impl Display for Pretty<'_, ProgrammableMoveCall> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(move_call) = self; let ProgrammableMoveCall { @@ -231,7 +231,7 @@ impl<'a> Display for Pretty<'a, ProgrammableMoveCall> { } } -impl<'a> Display for Pretty<'a, Argument> { +impl Display for Pretty<'_, Argument> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(argument) = self; @@ -244,7 +244,7 @@ impl<'a> Display for Pretty<'a, Argument> { write!(f, "{}", output) } } -impl<'a> Display for Pretty<'a, ResolvedResults> { +impl Display for Pretty<'_, ResolvedResults> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(ResolvedResults { mutable_reference_outputs, @@ -283,7 +283,7 @@ impl<'a> Display for Pretty<'a, ResolvedResults> { } } -impl<'a> Display for Pretty<'a, TypeTag> { +impl Display for Pretty<'_, TypeTag> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(type_tag) = self; match type_tag { diff --git a/crates/iota-replay/src/replay.rs b/crates/iota-replay/src/replay.rs index a2e7ddff8d2..0c40b5c52ff 100644 --- a/crates/iota-replay/src/replay.rs +++ b/crates/iota-replay/src/replay.rs @@ -727,18 +727,7 @@ impl LocalExec { reason: "System transaction".to_string(), }); } - // Before protocol version 16, the generation of effects depends on the wrapped - // tombstones. It is not possible to retrieve such data for replay. - if tx_info.protocol_version.as_u64() < 16 { - warn!( - "Protocol version ({:?}) too old: {}, skipping transaction", - tx_info.protocol_version, tx_digest - ); - return Err(ReplayEngineError::TransactionNotSupported { - digest: *tx_digest, - reason: "Protocol version too old".to_string(), - }); - } + // Initialize the state necessary for execution // Get the input objects let input_objects = self.initialize_execution_env_state(tx_info).await?; diff --git a/crates/iota-replay/src/tests.rs b/crates/iota-replay/src/tests.rs index a3759dcd479..73114511319 100644 --- a/crates/iota-replay/src/tests.rs +++ b/crates/iota-replay/src/tests.rs @@ -16,11 +16,10 @@ use crate::{ /// Keep searching for non-system TXs in the checkppints for this long /// Very unlikely to take this long, but we want to be sure we find one -const NUM_CHECKPOINTS_TO_ATTEMPT: usize = 1_000; +const NUM_CHECKPOINTS_TO_ATTEMPT: usize = 10_000; /// Checks that replaying the latest tx on each testnet and mainnet does not /// fail -#[ignore] #[tokio::test] async fn verify_latest_tx_replay_testnet_mainnet() { let _ = verify_latest_tx_replay_impl().await; @@ -30,7 +29,8 @@ async fn verify_latest_tx_replay_impl() { let urls: Vec<_> = default_cfg .base_network_configs .iter() - .filter(|q| q.name != "devnet") // Devnet is not always stable + // TODO: enable this when mainnet is launched + .filter(|q| q.name != "devnet" && q.name != "mainnet") // Devnet is not always stable, mainnet is not ready .map(|c| c.public_full_node.clone()) .collect(); @@ -60,7 +60,7 @@ async fn verify_latest_tx_replay_impl() { .unwrap() .transactions; - let mut non_system_txs = extract_one_system_tx(&rpc_client, txs).await; + let mut non_system_txs = extract_one_no_system_tx(&rpc_client, txs).await; num_checkpoint_trials_left -= 1; while non_system_txs.is_none() && num_checkpoint_trials_left > 0 { num_checkpoint_trials_left -= 1; @@ -71,7 +71,7 @@ async fn verify_latest_tx_replay_impl() { .await .unwrap() .transactions; - non_system_txs = extract_one_system_tx(&rpc_client, txs).await; + non_system_txs = extract_one_no_system_tx(&rpc_client, txs).await; } if non_system_txs.is_none() { @@ -101,7 +101,7 @@ async fn verify_latest_tx_replay_impl() { } } -async fn extract_one_system_tx( +async fn extract_one_no_system_tx( rpc_client: &IotaClient, mut txs: Vec<TransactionDigest>, ) -> Option<TransactionDigest> { diff --git a/crates/iota-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap b/crates/iota-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap index 560f4a795b4..1011bf3fa69 100644 --- a/crates/iota-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap +++ b/crates/iota-swarm-config/tests/snapshots/snapshot_tests__genesis_config_snapshot_matches.snap @@ -1,12 +1,13 @@ --- source: crates/iota-swarm-config/tests/snapshot_tests.rs expression: genesis_config +snapshot_kind: text --- ssfn_config_info: ~ validator_config_info: ~ parameters: chain_start_timestamp_ms: 0 - protocol_version: 1 + protocol_version: 2 allow_insertion_of_extra_objects: true epoch_duration_ms: 86400000 accounts: diff --git a/crates/iota-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap b/crates/iota-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap index 147ad24674c..c4a877d419f 100644 --- a/crates/iota-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap +++ b/crates/iota-swarm-config/tests/snapshots/snapshot_tests__populated_genesis_snapshot_matches-2.snap @@ -3,12 +3,12 @@ source: crates/iota-swarm-config/tests/snapshot_tests.rs expression: genesis.iota_system_object().into_genesis_version_for_tooling() --- epoch: 0 -protocol_version: 1 +protocol_version: 2 system_state_version: 1 iota_treasury_cap: inner: id: - id: "0x934856dbf1a6032f66ee5ff2e959767c8efc379e69bd3087e78cdb051df2f876" + id: "0xc0a3df051577bcc903e0199da102f7f6f2a91efd10b974cf02abf758c940b2c4" total_supply: value: "751500000000000000" validators: @@ -244,13 +244,13 @@ validators: next_epoch_primary_address: ~ extra_fields: id: - id: "0xe8024a1572d13ddd22144a52f5bb90f9d93f0057d8a7f6886833a8558c5f42dc" + id: "0xaed6692f9414d47c421b568a78db64aad24df4ec5f907da2df9691ca8f8d8060" size: 0 voting_power: 10000 - operation_cap_id: "0xced7229b5da6ab4e03dbd482bfc0d31444f1530d0ae1020d55531f80051034de" + operation_cap_id: "0x27c7b6a423462fd937f9592b0a6460552d6dbe39529af7b31c1078d4a4278cee" gas_price: 1000 staking_pool: - id: "0x7bc82accfac70f4e347e02c215f509b1b22a1ac63f5edd784d16d0e95e1e3840" + id: "0x6d23d6ca509804be5b4366dfc8999117cee38954b222aa1869d2cd544caf65e1" activation_epoch: 0 deactivation_epoch: ~ iota_balance: 1500000000000000 @@ -258,14 +258,14 @@ validators: value: 0 pool_token_balance: 1500000000000000 exchange_rates: - id: "0xe82a41af6638521838d31f97bc54c191f0ce1a1781c35ac3d9a4e8499494e0c2" + id: "0x40c803c53c8e36a656201bc8742de9e8fac5fa2ed6b26b66e2698e1f221d6558" size: 1 pending_stake: 0 pending_total_iota_withdraw: 0 pending_pool_token_withdraw: 0 extra_fields: id: - id: "0x3589b1f00ea0f4019cd46b56d29b2286c1ec783e52e84415008747fb1d8c9043" + id: "0x783db4410eb618c9d4c739a0e8045a9ec4724f7ec4aeed22a298d682abffe508" size: 0 commission_rate: 200 next_epoch_stake: 1500000000000000 @@ -273,27 +273,27 @@ validators: next_epoch_commission_rate: 200 extra_fields: id: - id: "0x83acc826eb238b984c7bae9e964952dd72902a031d1f555cc4b0676495fc930e" + id: "0x73293ec9fb51bd0ad2cc3ec7b735fc274dbbef266c2e5a49425ab3940c494fb6" size: 0 pending_active_validators: contents: - id: "0xb98e2c63a08a2c27b9d14288105e784144c65dfdb081951e9794d385dcdf378b" + id: "0xd66fd1d251b3d564a7426c73cc67ca639b7423090081fcf79eb3dc0b40f63a7c" size: 0 pending_removals: [] staking_pool_mappings: - id: "0x17d4a3fab108e0275cfbd2b1966444e99419185643d340b5d1b425faab103cee" + id: "0x014b69d82717bfeb39f32586c065cad188d0fedc5c071bdcc710aaab2a245b7a" size: 1 inactive_validators: - id: "0xe7da2643c898e967489e2227ed2d36a0c8db61410e937dcfea7d915065736bc8" + id: "0x701c488df2bbfb7eae3ec279ad32702870a5750e6cd7f514c781c0859e916424" size: 0 validator_candidates: - id: "0x7c28f48d2427b54a9b018b62a2caaadd39ce209d779396f8ee23a4f7fc8d4901" + id: "0x39c06666a86631d172a296315d8c536add0cb612764a14be70ba269593527253" size: 0 at_risk_validators: contents: [] extra_fields: id: - id: "0x520c8bed0efe1861657f5b33eea0745aeeabfd4d1887172831a99c4fed274b4d" + id: "0x3db8221a6b41c7a07005b9cf9ed2b71cb45a0512181ed26e45422b1a56842a86" size: 0 storage_fund: total_object_storage_rebates: @@ -310,7 +310,7 @@ parameters: validator_low_stake_grace_period: 7 extra_fields: id: - id: "0x4b729e195cfcf8f19c9508866d464aea19e48db9a721114e5a6159d53abf310f" + id: "0xc3250b13a30f434b6fe6ac4f7747b761d7e06b377619f73e9c044fb6fbcb9043" size: 0 iota_system_admin_cap: dummy_field: false @@ -327,5 +327,5 @@ safe_mode_non_refundable_storage_fee: 0 epoch_start_timestamp_ms: 10 extra_fields: id: - id: "0x3ff2b204a8ce9d05936b477dbcf16285afbd069216734afbe1ae0798be0bb0e4" + id: "0x03afbe024fb0454cdd8498212b32b5c4f4000bb7335e4a54f2f1415481d68e3a" size: 0 diff --git a/crates/iota-transactional-test-runner/src/test_adapter.rs b/crates/iota-transactional-test-runner/src/test_adapter.rs index 2e70b822ee8..268fe616239 100644 --- a/crates/iota-transactional-test-runner/src/test_adapter.rs +++ b/crates/iota-transactional-test-runner/src/test_adapter.rs @@ -172,7 +172,7 @@ struct TxnSummary { } #[async_trait] -impl<'a> MoveTestAdapter<'a> for IotaTestAdapter { +impl MoveTestAdapter<'_> for IotaTestAdapter { type ExtraPublishArgs = IotaPublishArgs; type ExtraRunArgs = IotaRunArgs; type ExtraInitArgs = IotaInitArgs; diff --git a/crates/iota/src/client_ptb/ast.rs b/crates/iota/src/client_ptb/ast.rs index ed6f98bc3b4..e07b2dec8d7 100644 --- a/crates/iota/src/client_ptb/ast.rs +++ b/crates/iota/src/client_ptb/ast.rs @@ -430,7 +430,7 @@ impl fmt::Display for ParsedPTBCommand { struct TyDisplay<'a>(&'a ParsedType); -impl<'a> fmt::Display for TyDisplay<'a> { +impl fmt::Display for TyDisplay<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { use ParsedType::*; match self.0 { diff --git a/crates/iota/src/client_ptb/token.rs b/crates/iota/src/client_ptb/token.rs index c4e184eb288..6cae8f1ddcf 100644 --- a/crates/iota/src/client_ptb/token.rs +++ b/crates/iota/src/client_ptb/token.rs @@ -65,7 +65,7 @@ pub enum Token { Upgrade, } -impl<'l> Lexeme<'l> { +impl Lexeme<'_> { /// Returns true if this lexeme corresponds to a special error token. pub fn is_error(&self) -> bool { use Token as T; @@ -84,7 +84,7 @@ impl<'l> Lexeme<'l> { } } -impl<'a> fmt::Display for Lexeme<'a> { +impl fmt::Display for Lexeme<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Token as T; diff --git a/crates/iota/src/displays/dry_run_tx_block.rs b/crates/iota/src/displays/dry_run_tx_block.rs index 44f0727849d..8f4f5d3d217 100644 --- a/crates/iota/src/displays/dry_run_tx_block.rs +++ b/crates/iota/src/displays/dry_run_tx_block.rs @@ -14,7 +14,7 @@ use tabled::{ }; use crate::{client_commands::estimate_gas_budget_from_gas_cost, displays::Pretty}; -impl<'a> Display for Pretty<'a, DryRunTransactionBlockResponse> { +impl Display for Pretty<'_, DryRunTransactionBlockResponse> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(response) = self; diff --git a/crates/iota/src/displays/gas_cost_summary.rs b/crates/iota/src/displays/gas_cost_summary.rs index 8880db90553..7a0fea44fcf 100644 --- a/crates/iota/src/displays/gas_cost_summary.rs +++ b/crates/iota/src/displays/gas_cost_summary.rs @@ -8,7 +8,7 @@ use iota_types::gas::GasCostSummary; use crate::displays::Pretty; -impl<'a> Display for Pretty<'a, GasCostSummary> { +impl Display for Pretty<'_, GasCostSummary> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(gcs) = self; let GasCostSummary { diff --git a/crates/iota/src/displays/ptb_preview.rs b/crates/iota/src/displays/ptb_preview.rs index 70f6bd7d9f2..c6454ee7b2e 100644 --- a/crates/iota/src/displays/ptb_preview.rs +++ b/crates/iota/src/displays/ptb_preview.rs @@ -17,7 +17,7 @@ use crate::{ sp, }; -impl<'a> Display for PTBPreview<'a> { +impl Display for PTBPreview<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let mut builder = TableBuilder::default(); let columns = vec!["command", "values"]; diff --git a/crates/iota/src/displays/status.rs b/crates/iota/src/displays/status.rs index ba17d6de4cb..debe8ee3747 100644 --- a/crates/iota/src/displays/status.rs +++ b/crates/iota/src/displays/status.rs @@ -8,7 +8,7 @@ use iota_json_rpc_types::IotaExecutionStatus::{self, Failure, Success}; use crate::displays::Pretty; -impl<'a> Display for Pretty<'a, IotaExecutionStatus> { +impl Display for Pretty<'_, IotaExecutionStatus> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let Pretty(status) = self; diff --git a/crates/iota/src/displays/summary.rs b/crates/iota/src/displays/summary.rs index a8db355b876..f886f2e2360 100644 --- a/crates/iota/src/displays/summary.rs +++ b/crates/iota/src/displays/summary.rs @@ -11,7 +11,7 @@ use tabled::{ use crate::{client_ptb::ptb::Summary, displays::Pretty}; -impl<'a> Display for Pretty<'a, Summary> { +impl Display for Pretty<'_, Summary> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let mut builder = TableBuilder::default(); let Pretty(summary) = self; diff --git a/crates/iota/src/unit_tests/profiler_tests.rs b/crates/iota/src/unit_tests/profiler_tests.rs index f9b6aadf208..ac053d7b032 100644 --- a/crates/iota/src/unit_tests/profiler_tests.rs +++ b/crates/iota/src/unit_tests/profiler_tests.rs @@ -36,7 +36,6 @@ fn test_macro_shows_feature_enabled() { } } -#[ignore] #[cfg(feature = "gas-profiler")] #[tokio::test(flavor = "multi_thread")] async fn test_profiler() { @@ -49,7 +48,7 @@ async fn test_profiler() { let profile_output = output_dir.path().join("profile.json"); let testnet_url = "https://api.testnet.iota.cafe".to_string(); - let tx_digest = "98KxVD14f2JgceKx4X27HaVAA2YGJ3Aazf6Y4tabpHa8".to_string(); + let tx_digest = "7qq4W43TqHg9tQPMvdAFW4Tz6J88KnPppBPR1hNKmQAd".to_string(); let cmd = ReplayToolCommand::ProfileTransaction { tx_digest, diff --git a/crates/typed-store/src/rocks/iter.rs b/crates/typed-store/src/rocks/iter.rs index 68e76d5cf61..87766b68f7c 100644 --- a/crates/typed-store/src/rocks/iter.rs +++ b/crates/typed-store/src/rocks/iter.rs @@ -57,7 +57,7 @@ impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iter<'a, K, V> { } } -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for Iter<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for Iter<'_, K, V> { type Item = (K, V); fn next(&mut self) -> Option<Self::Item> { @@ -94,7 +94,7 @@ impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for Iter<'a, K, V> { } } -impl<'a, K, V> Drop for Iter<'a, K, V> { +impl<K, V> Drop for Iter<'_, K, V> { fn drop(&mut self) { if let Some(bytes_scanned) = self.bytes_scanned.take() { bytes_scanned.observe(self.bytes_scanned_counter as f64); @@ -166,7 +166,7 @@ impl<'a, K, V> RevIter<'a, K, V> { } } -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for RevIter<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for RevIter<'_, K, V> { type Item = (K, V); /// Will give the next item backwards diff --git a/crates/typed-store/src/rocks/keys.rs b/crates/typed-store/src/rocks/keys.rs index a50a52160ef..21be69b8a28 100644 --- a/crates/typed-store/src/rocks/keys.rs +++ b/crates/typed-store/src/rocks/keys.rs @@ -24,7 +24,7 @@ impl<'a, K: DeserializeOwned> Keys<'a, K> { } } -impl<'a, K: DeserializeOwned> Iterator for Keys<'a, K> { +impl<K: DeserializeOwned> Iterator for Keys<'_, K> { type Item = Result<K, TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { @@ -44,7 +44,7 @@ impl<'a, K: DeserializeOwned> Iterator for Keys<'a, K> { } } -impl<'a, K: Serialize> Keys<'a, K> { +impl<K: Serialize> Keys<'_, K> { /// Skips all the elements that are smaller than the given key, /// and either lands on the key or the first one greater than /// the key. diff --git a/crates/typed-store/src/rocks/mod.rs b/crates/typed-store/src/rocks/mod.rs index 619ab943cf1..d142afddf15 100644 --- a/crates/typed-store/src/rocks/mod.rs +++ b/crates/typed-store/src/rocks/mod.rs @@ -1795,7 +1795,7 @@ pub enum RocksDBRawIter<'a> { ), } -impl<'a> RocksDBRawIter<'a> { +impl RocksDBRawIter<'_> { pub fn valid(&self) -> bool { delegate_iter_call!(self.valid()) } @@ -1835,7 +1835,7 @@ pub enum RocksDBIter<'a> { ), } -impl<'a> Iterator for RocksDBIter<'a> { +impl Iterator for RocksDBIter<'_> { type Item = Result<(Box<[u8]>, Box<[u8]>), Error>; fn next(&mut self) -> Option<Self::Item> { match self { diff --git a/crates/typed-store/src/rocks/safe_iter.rs b/crates/typed-store/src/rocks/safe_iter.rs index f38ca5e6909..88efa9cb20f 100644 --- a/crates/typed-store/src/rocks/safe_iter.rs +++ b/crates/typed-store/src/rocks/safe_iter.rs @@ -55,7 +55,7 @@ impl<'a, K: DeserializeOwned, V: DeserializeOwned> SafeIter<'a, K, V> { } } -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for SafeIter<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for SafeIter<'_, K, V> { type Item = Result<(K, V), TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { @@ -95,7 +95,7 @@ impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for SafeIter<'a, K, } } -impl<'a, K, V> Drop for SafeIter<'a, K, V> { +impl<K, V> Drop for SafeIter<'_, K, V> { fn drop(&mut self) { if let Some(bytes_scanned) = self.bytes_scanned.take() { bytes_scanned.observe(self.bytes_scanned_counter as f64); @@ -160,7 +160,7 @@ impl<'a, K, V> SafeRevIter<'a, K, V> { } } -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for SafeRevIter<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for SafeRevIter<'_, K, V> { type Item = Result<(K, V), TypedStoreError>; /// Will give the next item backwards diff --git a/crates/typed-store/src/rocks/tests.rs b/crates/typed-store/src/rocks/tests.rs index 8aa3aef5771..c6e82425622 100644 --- a/crates/typed-store/src/rocks/tests.rs +++ b/crates/typed-store/src/rocks/tests.rs @@ -35,7 +35,7 @@ enum TestIteratorWrapper<'a, K, V> { // for different types of Iterator. For non-safe Iterator, it returns the key // value pair. For SafeIterator, it consumes the result (assuming no error), and // return they key value pairs. -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for TestIteratorWrapper<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for TestIteratorWrapper<'_, K, V> { type Item = (K, V); fn next(&mut self) -> Option<Self::Item> { match self { @@ -92,7 +92,7 @@ where } } -impl<'a, K: Serialize, V> TestIteratorWrapper<'a, K, V> { +impl<K: Serialize, V> TestIteratorWrapper<'_, K, V> { pub fn skip_to(self, key: &K) -> Result<Self, TypedStoreError> { match self { TestIteratorWrapper::Iter(iter) => Ok(TestIteratorWrapper::Iter(iter.skip_to(key)?)), diff --git a/crates/typed-store/src/rocks/values.rs b/crates/typed-store/src/rocks/values.rs index 0dfa38d9ed0..f3616a511b0 100644 --- a/crates/typed-store/src/rocks/values.rs +++ b/crates/typed-store/src/rocks/values.rs @@ -24,7 +24,7 @@ impl<'a, V: DeserializeOwned> Values<'a, V> { } } -impl<'a, V: DeserializeOwned> Iterator for Values<'a, V> { +impl<V: DeserializeOwned> Iterator for Values<'_, V> { type Item = Result<V, TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { diff --git a/crates/typed-store/src/sally/mod.rs b/crates/typed-store/src/sally/mod.rs index 7e535e0761f..2766865b64c 100644 --- a/crates/typed-store/src/sally/mod.rs +++ b/crates/typed-store/src/sally/mod.rs @@ -427,7 +427,7 @@ pub enum SallyIter<'a, K, V> { TestDB(TestDBIter<'a, K, V>), } -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for SallyIter<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for SallyIter<'_, K, V> { type Item = Result<(K, V), TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { match self { @@ -485,7 +485,7 @@ pub enum SallyRevIter<'a, K, V> { TestDB(TestDBRevIter<'a, K, V>), } -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for SallyRevIter<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for SallyRevIter<'_, K, V> { type Item = Result<(K, V), TypedStoreError>; /// Will give the next item backwards @@ -504,7 +504,7 @@ pub enum SallyKeys<'a, K> { TestDB(TestDBKeys<'a, K>), } -impl<'a, K: DeserializeOwned> Iterator for SallyKeys<'a, K> { +impl<K: DeserializeOwned> Iterator for SallyKeys<'_, K> { type Item = Result<K, TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { @@ -522,7 +522,7 @@ pub enum SallyValues<'a, V> { TestDB(TestDBValues<'a, V>), } -impl<'a, V: DeserializeOwned> Iterator for SallyValues<'a, V> { +impl<V: DeserializeOwned> Iterator for SallyValues<'_, V> { type Item = Result<V, TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { diff --git a/crates/typed-store/src/test_db.rs b/crates/typed-store/src/test_db.rs index 2225abb2124..01c17504ce9 100644 --- a/crates/typed-store/src/test_db.rs +++ b/crates/typed-store/src/test_db.rs @@ -74,7 +74,7 @@ pub struct TestDBValues<'a, V> { phantom: PhantomData<V>, } -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for TestDBIter<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for TestDBIter<'_, K, V> { type Item = Result<(K, V), TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { @@ -173,7 +173,7 @@ impl<'a, K, V> TestDBRevIter<'a, K, V> { } } -impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for TestDBRevIter<'a, K, V> { +impl<K: DeserializeOwned, V: DeserializeOwned> Iterator for TestDBRevIter<'_, K, V> { type Item = Result<(K, V), TypedStoreError>; /// Will give the next item backwards @@ -182,7 +182,7 @@ impl<'a, K: DeserializeOwned, V: DeserializeOwned> Iterator for TestDBRevIter<'a } } -impl<'a, K: DeserializeOwned> Iterator for TestDBKeys<'a, K> { +impl<K: DeserializeOwned> Iterator for TestDBKeys<'_, K> { type Item = Result<K, TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { @@ -200,7 +200,7 @@ impl<'a, K: DeserializeOwned> Iterator for TestDBKeys<'a, K> { } } -impl<'a, V: DeserializeOwned> Iterator for TestDBValues<'a, V> { +impl<V: DeserializeOwned> Iterator for TestDBValues<'_, V> { type Item = Result<V, TypedStoreError>; fn next(&mut self) -> Option<Self::Item> { diff --git a/docs/content/_snippets/not-available-on-testnet.mdx b/docs/content/_snippets/not-available-on-testnet.mdx index 3d1956f3f15..fee649cb633 100644 --- a/docs/content/_snippets/not-available-on-testnet.mdx +++ b/docs/content/_snippets/not-available-on-testnet.mdx @@ -1,5 +1,5 @@ :::info Mainnet Only -IOTA EVM and IOTA Identity are not available on the IOTA Move Testnet. +IOTA EVM is not available on the IOTA Move Testnet. ::: \ No newline at end of file diff --git a/docs/content/developer/developer.mdx b/docs/content/developer/developer.mdx index 9fe0a348d29..3da2678b84f 100644 --- a/docs/content/developer/developer.mdx +++ b/docs/content/developer/developer.mdx @@ -44,6 +44,12 @@ Go to [Cryptography](cryptography.mdx). Get aquainted with IOTA by solving challenges of increasing complexity in the [Capture the Flag section](iota-move-ctf/introduction.mdx). +## Decentralized Identity + +IOTA offers a Decentralized Identity framework which can be utilized to provide and use digital decentralized identities for People, Organizations, Things and Objects. + +Go to the [IOTA Identity Framework](/iota-identity/) section. + ## Advanced Topics The Advanced Topics section includes guides for advanced solutions (like asset tokenization). These topics assume you are familiar with Move and the IOTA blockchain. @@ -75,12 +81,3 @@ Go to the [Developer Cheat Sheet](dev-cheat-sheet.mdx). Everything you need to know about our Layer 2 EVM Support, including working with the IOTA EVM and ShimmerEVM chains. Go to [EVM Smart Contracts](https://wiki.iota.org/isc/introduction/). - -## Decentralized Identity - -<NotAvailableOnTestnet /> - -IOTA also offers a Decentralized Identity framework which can be utilized to provide and use digital decentralized identities for People, Organizations, Things and Objects. - -Go to the [IOTA Identity Framework](https://wiki.iota.org/identity.rs/welcome/) section. - diff --git a/docs/content/developer/getting-started/iota-environment.mdx b/docs/content/developer/getting-started/iota-environment.mdx index 3c3360d1cf9..1d8a50aeefd 100644 --- a/docs/content/developer/getting-started/iota-environment.mdx +++ b/docs/content/developer/getting-started/iota-environment.mdx @@ -34,6 +34,4 @@ IOTA provides two official SDKs that you can use to interact with the IOTA netwo ## Move IDEs and plugins -We recommend that you use the [Visual Studio Code](https://code.visualstudio.com/) IDE. - -We are currently developing a code analyzer plugin for Visual Studio Code - details to follow soon. \ No newline at end of file +We recommend that you use the [Visual Studio Code](https://code.visualstudio.com/) IDE with the [IOTA Move](https://marketplace.visualstudio.com/items?itemName=iotaledger.iota-move) extension installed. \ No newline at end of file diff --git a/docs/content/developer/iota-101/nft/marketplace.mdx b/docs/content/developer/iota-101/nft/marketplace.mdx new file mode 100644 index 00000000000..e5c20e2fa67 --- /dev/null +++ b/docs/content/developer/iota-101/nft/marketplace.mdx @@ -0,0 +1,17 @@ +--- +description: A brief introduction to implementing NFT marketplace extension using the Kiosk Apps standard in IOTA's Move language. +--- + + + + +import Marketplace from '../../../../examples/move/nft_marketplace/README.md'; + +# Marketplace Extension + +The Marketplace Extension for [IOTA Kiosk](../../standards/kiosk.mdx) is a customizable framework that extends the functionality of the IOTA Kiosk by enabling efficient asset trading with integrated royalty management and pricing mechanism. + +Kiosk owners can list items for sale by setting prices, and enforce royalties, ensuring creators receive a percentage of each sale. Buyers can securely purchase items, with the extension validating payments and handling royalties automatically. All transactions are governed by robust transfer policies, ensuring security and compliance. + +<Marketplace /> + diff --git a/docs/content/developer/iota-101/nft/rent-nft.mdx b/docs/content/developer/iota-101/nft/rent-nft.mdx index df7f28a81c3..d4e93624b30 100644 --- a/docs/content/developer/iota-101/nft/rent-nft.mdx +++ b/docs/content/developer/iota-101/nft/rent-nft.mdx @@ -77,6 +77,11 @@ The rental smart contract utilizes th [Kiosk Apps](../../standards/kiosk-apps.md Both lenders and borrowers must install a Kiosk extension to participate. Additionally, the creator of the NFT type must create a rental policy and a `ProtectedTP` object to allow the extension to manage rentals while enforcing royalties. +## Move Module Details + +The NFT rental functionality is implemented in a single Move module: `nft_rental.move`. +You can find the source code in the [IOTA repository](https://github.com/iotaledger/iota/tree/develop/docs/examples/move/nft_marketplace/sources/rental_extension.move) under the `examples` directory. The code includes comments to help you understand the logic and structure. + ### The `nft_rental` Module The `nft_rental` module provides an API for: diff --git a/docs/content/developer/standards/kiosk-apps.mdx b/docs/content/developer/standards/kiosk-apps.mdx index 116cdccf28f..cb88f6eab24 100644 --- a/docs/content/developer/standards/kiosk-apps.mdx +++ b/docs/content/developer/standards/kiosk-apps.mdx @@ -259,5 +259,8 @@ txb.moveCall({ ## Related links - [NFT Rental](../iota-101/nft/rent-nft.mdx): An example implementation of the Kiosk Apps standard that enables renting NFTs. +- [NFT Rental Extension](https://github.com/iotaledger/iota/tree/develop/docs/examples/move/nft_marketplace/sources/rental_extension.move): An example implementation of the Kiosk Extension standard that enables renting NFTs. +- [Marketplace](../iota-101/nft/marketplace.mdx): An example implementation of the Kiosk Apps standard that enables marketplace functionality. +- [Marketplace Extension](https://github.com/iotaledger/iota/tree/develop/docs/examples/move/nft_marketplace/sources/marketplace_extension.move): Move module that contains the source code(Kiosk extension) for the marketplace app. <Quiz questions={questions} /> \ No newline at end of file diff --git a/docs/content/iota-identity/contribute.mdx b/docs/content/iota-identity/contribute.mdx new file mode 100644 index 00000000000..f83dfae4a88 --- /dev/null +++ b/docs/content/iota-identity/contribute.mdx @@ -0,0 +1,35 @@ +--- +title: Contribute to the project +sidebar_label: Contribute +description: Contribute to IOTA Identity by joining the Identity initiative, via the projects GitHub repository, documentation, or sharing your knowledge. +image: /img/identity/icon.png +tags: + - reference + - identity +--- + +**Thanks for thinking about contributing to the project! You can contribute using the following ways.** + +## Contribute to the Project's GitHub Repository + +All of the code is open source and hosted on [GitHub](https://github.com/iotaledger/identity.rs) where you can: + +- [Report a bug](https://github.com/iotaledger/identity.rs/issues/new/choose). +- [Suggest a new feature](https://github.com/iotaledger/identity.rs/blob/main/.github/CONTRIBUTING.md). +- [Contribute to the documentation](#contribute-to-the-documentation). + +## Contribute to the Documentation + +This documentation is also open source and hosted on GitHub. + +If you want to contribute new documentation or fix an error, see the [contribution guidelines](https://docs.iota.org/references/contribute/contribution-process). + +## Share Your Knowledge + +Helping others is an important part of any open source ecosystem. + +By sharing your knowledge with others, you can provide a lot of value to the community and maybe inspire someone else to learn and contribute. + +Take a look at what discussions are going on in the `#identity` channel on [Discord](https://discord.iota.org). + +Thanks :heart: diff --git a/docs/content/iota-identity/explanations/about-identity-objects.mdx b/docs/content/iota-identity/explanations/about-identity-objects.mdx new file mode 100644 index 00000000000..7bff840d856 --- /dev/null +++ b/docs/content/iota-identity/explanations/about-identity-objects.mdx @@ -0,0 +1,47 @@ +--- +title: Identity object +description: The IOTA `Identity` Move object. +image: /img/identity/icon.png +tags: + - explanation + - identity + - did + - getting-started +--- + +# Identity Object + +An Identity Object is a **shared** _Move_ object that stores a single DID Document on the network, allowing both +on-chain and off-chain actors to interact with it. + +The Identity Object's unique ID is used to derive the actual DID of the DID Document it contains. + +## Identity's Access Control + +An `Identity` is a shared object and is thus accessible by anyone. For this reason, it is of paramount importance to +limit the use of an `Identity`'s APIs - especially its mutable ones - to only the intended actors. + +### Controllers and `ControllerCap` + +The [DID specification](https://www.w3.org/TR/did-core/) defines **DID controller** as the entity allowed to make changes to +a DID document. Since an `Identity` may be viewed as a simple wrapper over a DID Document, we extend the definition of _DID controller_ to be: +_the entity allowed to make changes to an `Identity`_. + +In order to allow a broader range of actors to act as an `Identity`'s controller - e.g. an `Identity` controlling another `Identity` - +a controller is identified through its **controller capability** `ControllerCap`. +A `ControllerCap` is a _Move_ object that acts as a blind token, allowing anyone who presents it to the corresponding `Identity` to access +the `Identity`'s APIs that are only invocable by its controllers. + +### Voting power and threshold + +When an `Identity` has more than a single controller, an access control (AC) policy for the `Identity` must be established. Such a policy is +represented by an unsigned integer **threshold** that is established upon the `Identity`'s creation. Together with the `Identity`'s threshold, each controller +is assigned with a **voting power**, i.e. an unsigned integer that acts as a sort of _weight_ in the evaluation of an AC control policy. + +### Proposals + +Whenever a controller wants to make a change to its `Identity`, a **proposal** encapsulating that change is created instead of carrying out the update +right away. In order to execute the changes contained in a `Proposal` enough controllers need to approve it. The exact number of approvals depends on +the controllers' voting powers as well as the `Identity`'s threshold. +`Proposal`s keep track of the approvals they receive by internally storing the sum of all approving controllers' voting powers in a field called `votes`. +A `Proposal` can only be executed after `votes` exceeds `Identity`'s threshold. diff --git a/docs/content/iota-identity/explanations/decentralized-identifiers.mdx b/docs/content/iota-identity/explanations/decentralized-identifiers.mdx new file mode 100644 index 00000000000..30e7b38f7db --- /dev/null +++ b/docs/content/iota-identity/explanations/decentralized-identifiers.mdx @@ -0,0 +1,135 @@ +--- +description: The Decentralized Identifiers (DID) standard from W3C is the fundamental standard that supports the concept of a decentralized digital identity. Explore the basic aspects of the DID standard. +image: /img/identity/icon.png +tags: + - explanation + - identity + - did + - getting-started + - verification-method + - verifiable-credentials +--- + +# Decentralized Identifiers (DID) + +:::info TL;DR + +DIDs are unique identifiers that can be resolved to DID Documents containing public keys and URIs for identity verification and public information. + +Adhering to the W3C's DID specifications, IOTA's implementation ensures interoperability and security within its ledger. + +DIDs support self-sovereign identity, allowing identity owners to control their creation and destruction, +while facilitating encrypted communication. + +::: + +A DID is a unique identifier that can be resolved to a DID Document. This document contains data such as public keys, enabling the holder to prove ownership over their personal data, but also URIs that link to public information about the identity. DIDs are the fundamental building blocks of decentralized digital identity. +This implementation complies with the [DID specifications v1.0](https://www.w3.org/TR/did-core//) from the World Wide Web Consortium (W3C). + +In the IOTA Identity framework, we have implemented the DID standard according to the `iota` [DID Method Specification](../../references/iota-identity/iota-did-method-spec.mdx). Other implementations of DID on IOTA must follow the `iota` DID Method Specification if they want to use the `iota` method name. Libraries implementing the `iota` DID Method Specification are provided for [Rust](../getting-started/rust.mdx). + +## Decentralized Identifiers + +A Decentralized Identifier, or DID, is a unique identifier that is tied to a subject. This subject can be anything, like a person, an organization, an IoT device, or even an object. The identifier can be used by the subject to identify themselves through a digital format, providing a basis for online identification. The identifier looks like a set of random characters that includes some prefixes to determine which standard and implementation is used: + +`did:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe` + +The World Wide Web Consortium (W3C) is a well-known standardization body that has standardized how DIDs should look and work. +This provides a basis for different technologies that implement the [DID standard](https://www.w3.org/TR/did-spec-registries/#did-methods) to achieve interoperability. +Keep in mind that, unfortunately, most of these methods are outdated and not maintained. + +## DID Documents + +The purpose of a DID is to help navigate to a DID Document, +which is a document containing more information regarding the identity subject. +This document contains data such as public keys, enabling the subject to prove ownership over their personal data, +but can contain additional information on how to interact with the subject. + +The identifier contains all information to resolve a DID, providing the latest DID Document. +The first three characters `did` indicate that the DID standard from W3C must be used to resolve the identifier. +It is followed by a unique method name, in our case `iota`, to indicate that the IOTA method is used. + +The IOTA method is a specific implementation following the [IOTA DID Method Specification](../../references/iota-identity/iota-did-method-spec.mdx). +This provides unique rules for the protocol to follow in order to manage a DID Document. +In our case, it describes how DID Documents are uploaded and queried to and from the IOTA ledger. + +Lastly, a DID also contains a set of random characters that are unique per identity and resolve to a single DID Document. + +:::tip Requires basic knowledge of Asymmetric Encryption + +The following sections require some basic knowledge of Asymmetric Encryption. +Please read or view some materials on the subject before continuing. + +::: + +### DID Document Anatomy + +A DID Document mostly contains two important pieces of data: verification methods and services. + +#### Verification Methods + +Verification methods contain public key material that can be used to prove ownership over the identity, +by cryptographically signing something with the associated private key. +The public key can be used to verify that the identity subject signed the data and therefore controls the private key. +Ownership over the private keys, therefore, proves ownership over the identity. + +This also means that it is very important to keep the private keys safe and secure. +In addition, the public keys allow users to send encrypted data to the identity that only the identity owner can decrypt. + +:::caution + +Never share your private keys, seeds, passphrases with anyone. Not even IOTA Foundation members. +This may lead to loss of funds or control over your own digital identity. + +::: + +#### Services + +Services are URIs that point to more information about the identity. +This could be something as simple as a website for an organizational identity. +These services are publicly available for all to read, +and should not contain Personal Identifiable Information (PII) in the case of human identities. + +## Why Use DIDs? + +DIDs allow any subject to have a unique identifier that they can prove ownership of, +and at the same time provide a way to send them encrypted messages. +The Identity is Self-Sovereign, meaning the subject is always in control; +whether it is [creating](../how-tos/decentralized-identifiers/create.mdx), [updating](../how-tos/decentralized-identifiers/update.mdx), or [destroying](../how-tos/decentralized-identifiers/delete.mdx) it. + +### Verifiable Credentials + +DIDs become more interesting when you combine them with [verifiable credentials (VC)](verifiable-credentials.mdx). +In essence, verifiable credentials (VCs) are signed statements by trusted third parties about a certain identity. +The signer, or Issuer, is referenced by the DID and so is the subject, often called the holder. +The holder controls a copy of this statement and can share it with other parties, the _Verifiers_, +who can verify the statement and check which party made the statement without asking the Issuer. +Instead, they can verify the issuer's signature by checking the issuer's DID Document. + +This puts Holders back in control over their own data, +while making the data much more trustworthy as it has become verifiable. + +## Why Use Iota Identity Over Other Implementations? + +IOTA Identity is a framework to implement Self-Sovereign Identities on IOTA. +Inherently, IOTA provides some unique features that have a major impact on the usability of the framework. + +### Availability and Accessibility + +DID Documents are stored in the ledger state and are covered storage deposit. +This guarantees that all nodes will have an up-to-date copy of the latest DID Document. +Resolving a DID into its document can usually be done by any IOTA node in the network. +This solves many issues regarding availability, accessibility, and synchronization. + +### Layer1 Interactions + +DID Documents are stored in [Identity objects](./about-identity-objects.mdx). +This allows them to directly interact with other Layer 1 artifacts like NFTs and coins. + +For instance, an Identity object representing a DID can hold coins or control NFTs. +Transferring funds between DIDs is also possible on Layer 1. + +### Ease-of-use + +IOTA Identity abstracts the details of the DID standard by providing easy-to-use APIs that allow standardized behavior. +It also allows more flexible and complex management of DID Documents. diff --git a/docs/content/iota-identity/explanations/verifiable-credentials.mdx b/docs/content/iota-identity/explanations/verifiable-credentials.mdx new file mode 100644 index 00000000000..257e6864dc1 --- /dev/null +++ b/docs/content/iota-identity/explanations/verifiable-credentials.mdx @@ -0,0 +1,74 @@ +--- +description: Verifiable credentials are statements about the holder. They can be verified online or in person, and the holder decides who to share them with. +image: /img/identity/icon.png +tags: + - explanation + - identity + - verifiable-credentials +--- +# Verifiable Credentials + +:::info TL;DR + +Verifiable credentials (VCs) are digital statements that can be cryptographically proven, like a digital passport, +and are used within systems to assert certain properties or capabilities of an entity. + +In the IOTA Identity framework, this is managed with decentralized identifiers (DIDs) on the IOTA network. +Subjects and issuers use their DIDs for the creation and verification of credentials. + +::: + +Credentials are statements about an entity, +such as properties that the entity possesses or capabilities that they have, +like a driver's license, passports, or a person's age. +Verifiable credentials (VCs) are statements (e.g., Alice has a driver's license) +that can be cryptographically verified by a third party, either online or in person. +Additionally, the holder of the VC decides what is shared and who it is shared with. + +There are several types of actors that play different roles in a Verifiable Credential system. +We'll start with a common example of how things work in the world today using physical credentials and centralized databases, +and outline the roles that various entities play in the Verifiable Credential system. + +## Example - Passport Issuance + +A government (the _issuer_) issues a passport asserting citizenship (the _Verifiable Credential_) to Alice (the _subject_ and _Holder_), +and writes the information to a database (the _Verifiable Data Registry_). +When crossing the border, Alice (the _Holder_) presents her passport to a border agent (the _Verifier_), +who can verify that Alice (the _subject_) is indeed a citizen. + + + +**Subject:** An entity about which claims are made – in this example, that Alice (the _subject_) is a citizen of this country. + +**Holder:** Any entity with a verifiable credential – Alice (the _Holder_) possesses the passport (the _VC_). + +**Issuer:** An entity which asserts claims about a subject – The governing body (the _issuer_), which is trusted, issues Alice a passport. + +**Verifier:** An entity which checks if the VC a holder presents is legitimate – The border agent (the _Verifier_) trusts the government (the _issuer_) which issued Alice her passport and validates that Alice (the _subject_) is a citizen. + +:::note + +See the [Verifiable Credentials Data Model 1.0 Specification](https://w3c.github.io/vc-data-model/) for more information. + +::: + +## Verifiable Credentials in IOTA + +In the IOTA Identity framework, instead of a physical passport being given to Alice and its information written +into a centralized database owned by the government, Alice receives a digital verifiable credential, +and the information required for verification in the future is written to the Tangle. + +At a high level, the creation and verification of a VC on IOTA works as follows: + + +The first step is to create a verifiable credential that requires the subject (Alice) and issuer (the government) to +have [DIDs](./decentralized-identifiers.mdx) published on the Tangle and a set of statements being asserted (that Alice has a passport). +The issuer signs the credential with their private key and publishes the public key to the Tangle. + +Once the issuer is confident that the credential satisfies its expectations, +the credential is stored and transmitted to the subject in a secure manner (off-chain). + +Validation is performed by looking up the issuer's public key on the Tangle, +the holder proving ownership of their DID to the verifier (evidence), +and validating that the issuing party has indeed signed the credential. + diff --git a/docs/content/iota-identity/explanations/verifiable-presentations.mdx b/docs/content/iota-identity/explanations/verifiable-presentations.mdx new file mode 100644 index 00000000000..5ae4bc6cb6e --- /dev/null +++ b/docs/content/iota-identity/explanations/verifiable-presentations.mdx @@ -0,0 +1,39 @@ +# Verifiable Presentations + +A verifiable presentation is the recommended data format for sharing one or more [verifiable credentials](./verifiable-credentials.mdx). +It is constructed and signed by a holder to prove control over their credentials and can be presented to a verifier for validation. + +For instance, after an issuer [creates and issues](./../how-tos/verifiable-credentials/create.mdx) a [verifiable credential](./verifiable-credentials.mdx) to a holder, such as a university issuing a degree to a graduate, +the holder stores it securely until asked to present it. +A company could then request proof of that university degree: the holder can [create a verifiable presentation](./../how-tos/verifiable-credentials/create.mdx) +containing their credential, already signed by their university, and present it to the company to [validate](./../how-tos/verifiable-credentials/create.mdx#validate-a-vc). + +Note that verifiable presentations that contain personal data should, as with verifiable credentials, be transmitted and stored securely off-chain to satisfy data privacy regulations such as [GDPR](https://gdpr.eu/). + +:::note + +See the [Verifiable Credentials Data Model Specification](https://www.w3.org/TR/vc-data-model/#presentations) for more information on verifiable presentations. + +::: + +## Security Considerations + +### Replay Attacks + +A malicious actor could potentially store a verifiable presentation without a challenge +and replayed to a different verifier, impersonating the holder. +This is because the holder's signature on a presentation would still be seen as valid indefinitely, +until they [rotate](https://www.w3.org/TR/did-core/#verification-method-rotation) the verification method used. + +To mitigate this, verifiers should always send a unique challenge when requesting a verifiable presentation. +This challenge can be set as the `nonce` property of the JWS by the holder during signing. +The digital signature prevents these properties from being altered as it would invalidate the signature, effectively preventing a malicious +actor from injecting different values into old verifiable presentations. A presentation without a challenge in its proof that matches what was +sent by the verifier should be considered invalid. + +The challenge string should be sufficiently random and unique for each verifiable presentation requested by a verifier to avoid +being predicted. + +Holders may additionally specify that their signature on a verifiable presentation expires after a short duration, as +per `JwtPresentationOptions`. However, verifiers and different implementations could ignore that property, +so setting a signature expiration alone should not be relied upon. diff --git a/docs/content/iota-identity/getting-started/rust.mdx b/docs/content/iota-identity/getting-started/rust.mdx new file mode 100644 index 00000000000..d32f74a84e1 --- /dev/null +++ b/docs/content/iota-identity/getting-started/rust.mdx @@ -0,0 +1,58 @@ +--- +sidebar_label: Rust +description: Getting started with the IOTA Identity Rust Library. +image: /img/identity/icon.png +tags: + - identity + - rust + - setup + - install + - getting-started +--- +# Getting Started with Rust + +## Requirements + +- [Rust](https://www.rust-lang.org/) (>= 1.62) +- [Cargo](https://doc.rust-lang.org/cargo/) (>= 1.62) + +## Include the Library + +To include IOTA Identity in your project, add it as a dependency in your `Cargo.toml`: + +```toml +[dependencies] +identity_iota = { git = "https://github.com/iotaledger/identity.rs", tag = "v1.6.0-alpha"} +``` + +## Examples + +To try out the [examples](https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples), you should: + +1. Clone the repository: + +```bash +git clone https://github.com/iotaledger/identity.rs +``` + +2. Build the repository: + +```bash +cargo build +``` + +3. Run your first example: + +```bash +cargo run --example 0_create_did +``` + +## API Reference + +You can find the API reference for the Rust library on [docs.rs](https://docs.rs/identity_iota/latest/identity_iota/index.html). + +If you would like to build the documentation, locally you can do so with the following command: + +``` +RUSTDOCFLAGS='--cfg docsrs' cargo +nightly doc -p identity_iota --all-features --no-deps --open +``` diff --git a/docs/content/iota-identity/how-tos/decentralized-identifiers/create.mdx b/docs/content/iota-identity/how-tos/decentralized-identifiers/create.mdx new file mode 100644 index 00000000000..11333042bb4 --- /dev/null +++ b/docs/content/iota-identity/how-tos/decentralized-identifiers/create.mdx @@ -0,0 +1,115 @@ +--- +title: Creating a Decentralized Identity +sidebar_label: Create and Publish +description: Create DID Documents and publish them to an IOTA network. +image: /img/identity/icon.png +tags: + - how-to + - identity + - did +--- + + +If you want to benefit from Self-Sovereign Identity, +you need to create a [Decentralized Identity](../../explanations/decentralized-identifiers.mdx). +This identity consists of many parts that have different functions. + +:::note DID method Specification + +Note that the Iota Identity Framework follows [IOTA DID Method Specification](../../../references/iota-identity/iota-did-method-spec.mdx). + +::: + +## Identity Generation Process + +### 1. Get Funds to cover the gas costs + +The first thing you will need to generate an identity is an address with enough funds to cover +the gas cost of the whole procedure. +In test networks as well as local ones, you can use a faucet to request funds. + +:::tip + +If you want to use the main IOTA network, +you will need an an address with actual IOTA funds to create a new Identity. + +::: + +<div className={'hide-code-block-extras'}> +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/utils/utils.rs#L99 +``` + +</TabItem> +</Tabs> +</div> + +### 2. Create the content for the DID Document + +As a bare minimum, a [DID document](../../explanations/decentralized-identifiers.mdx) needs at least one verification method. + +At this point, the DID itself is unknown since the `Identity` object is not published yet and thus there's no `ID` for it. + +:::tip + +You can use a placeholder `did:iota:0x0000000000000000000000000000000000000000000000000000000000000000` to reference +the DID inside the document. + +::: + +<div className={'hide-code-block-extras'}> +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/utils/utils.rs#L49-L58 +``` + +</TabItem> +</Tabs> +</div> + +### 3. Construct an `Identity` Object + +Next, you need to construct a new [Identity Object](../../explanations/about-identity-objects.mdx) that contains the just created +DID Document. +The created `Identity` contains an encoded version of the DID Document, and has a single controller that is represented by a newly +created `ControllerCap`. + +:::info Controller Capability +When a new `Identity` is created the transaction's sender is assumed to be its controller and thus a `ControllerCap` is sent to its address. +::: + +<div className={'hide-code-block-extras'}> +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/utils/utils.rs#L60-L64 +``` + +</TabItem> +</Tabs> +</div> + +## Full Example Code + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/0_basic/0_create_did.rs +``` + +</TabItem> +</Tabs> + +## Running Examples Locally + +In order to run the examples, you will need to run an IOTA network locally. + +If you want to use something different, you will need to modify the API and faucet endpoints in the examples to match your +setup. diff --git a/docs/content/iota-identity/how-tos/decentralized-identifiers/delete.mdx b/docs/content/iota-identity/how-tos/decentralized-identifiers/delete.mdx new file mode 100644 index 00000000000..9424433a6e4 --- /dev/null +++ b/docs/content/iota-identity/how-tos/decentralized-identifiers/delete.mdx @@ -0,0 +1,87 @@ +--- +title: Delete an IOTA Identity +sidebar_label: Delete +description: How to deactivate or destroy an IOTA Identity +image: /img/identity/icon.png +tags: + - how-to + - identity + - did +--- + + +There are two approaches to delete an IOTA Identity, with different implications: + +- [Deactivate](#deactivate) +- [Destroy](#destroy) + +## Deactivate + +As detailed in the [IOTA DID Method Specification]( +../../../references/iota-identity/iota-did-method-spec.mdx#deactivate), +a controller of an IOTA Identity may [deactivate](https://www.w3.org/TR/did-core/#did-document-metadata) it by executing an update that either: + +- deletes the contents of the DID Document entirely, leaving the state metadata empty, OR +- sets the `deactivated` field in the DID Document metadata to `true`. + +In both cases, the DID Document will be marked as `deactivated` when resolved. + +:::tip Reversible + +The identity can be reactivated at any time, by publishing an update restoring the DID Document's contents, +or unsetting the `deactivated` field in the metadata respectively, depending on how it was initially deactivated. + +::: + +### Example + +The following example demonstrates deactivating and reactivating an IOTA DID Document. + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/0_basic/3_deactivate_did.rs +``` + +</TabItem> +</Tabs> + +## Destroy + +Alternatively, you can [destroy](../../../references/iota-identity/iota-did-method-spec.mdx#destroy) an IOTA Identity permanently. + +:::warning Irreversible + +Destroying an IOTA Identity is permanent and irreversible. + +::: + +This may be achieved by a DID controller by executing the `Identity::execute_delete` API. + +Any coins and tokens owned by the destroyed `Identity` are reclaimed and must be sent to another address. + +:::warning + +Note that historical versions may still be stored off-ledger, or on a permanode, +so sensitive or Personal Identifiable Information (PII) should NEVER be stored in a DID Document. + +::: + +Even with a previous version available, a destroyed DID can never be restored. + +{/* +### Example + +The following example demonstrates how a controller destroys an IOTA Identity. + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/0_basic/4_delete_did.rs +``` + +</TabItem> +</Tabs> +*/} \ No newline at end of file diff --git a/docs/content/iota-identity/how-tos/decentralized-identifiers/resolve.mdx b/docs/content/iota-identity/how-tos/decentralized-identifiers/resolve.mdx new file mode 100644 index 00000000000..3f5bb7a23dd --- /dev/null +++ b/docs/content/iota-identity/how-tos/decentralized-identifiers/resolve.mdx @@ -0,0 +1,148 @@ +--- +title: Resolve an IOTA Identity +sidebar_label: Resolve +description: Explain how resolving works including arguments +image: /img/identity/icon.png +tags: + - how-to + - identity + - did +--- + + +DID resolution is the process of fetching and decoding a [DID Document](https://www.w3.org/TR/did-core/#dfn-did-documents) corresponding to a given [DID](https://www.w3.org/TR/did-core/#dfn-decentralized-identifiers). +The [IOTA Identity framework](https://github.com/iotaledger/identity.rs) supports resolving DID Documents that are +stored on an IOTA network and enables users to plug in handlers for additional methods. + +This is similar to, but not to be confused with, +the [W3C DID Resolution specification](https://w3c-ccg.github.io/did-resolution/), +which defines function signatures for resolution in the context of web or REST APIs, +whereas the IOTA Identity framework provides strongly-typed resolution for a better developer experience. + +This functionality is primarily provided by the `Resolver`, which can: + +- [Resolve IOTA DID Documents](#resolving-an-iota-did). +- [Resolve DID Documents from multiple DID methods](#resolving-multiple-did-methods). +- Resolve the DID Documents referenced in a verifiable presentation or credential. + +## Resolving an IOTA DID + +The following examples demonstrate how to resolve an IOTA DID Document from its DID. + +### Resolver + +Once you have configured a `Resolver` with a `Client`, it will resolve +IOTA DID Documents according to the read procedure defined in the [IOTA DID Method Specification](../../../references/iota-identity/iota-did-method-spec.mdx#read). +It fetches the `Identity` from the network specified in the DID (see [DID Format](../../../references/iota-identity/iota-did-method-spec.mdx#did-format)), +then extracts and validates the DID Document from it. + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust +use examples::create_did_document; +use examples::get_client_and_create_account; + +use examples::get_memstorage; +use identity_iota::iota::IotaDocument; +use identity_iota::prelude::Resolver; + +/// Demonstrates how to resolve an existing DID +#[tokio::main] +async fn main() -> anyhow::Result<()> { + // create new client to interact with chain and get funded account with keys + let storage = get_memstorage()?; + let identity_client = get_client_and_create_account(&storage).await?; + // create new DID document and publish it + let (document, _) = create_did_document(&identity_client, &storage).await?; + + let did = document.id().clone(); + + // We will be using a `Resolver` to resolve the DID Document. + let mut resolver = Resolver::<IotaDocument>::new(); + + // We need to register a handler that can resolve IOTA DIDs. + // This convenience method only requires us to provide a client. + resolver.attach_kinesis_iota_handler((*identity_client).clone()); + + let resolver_document: IotaDocument = resolver.resolve(&did).await.unwrap(); + + // Client and Resolver resolve to the same document. + assert_eq!(client_document, resolver_document); + + Ok(()) +} +``` + +</TabItem> +</Tabs> + +### Client + +You can also use the `Client` directly to resolve individual DIDs from its configured network. + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust +use examples::create_did_document; +use examples::get_client_and_create_account; +use examples::get_memstorage; + +use identity_iota::iota::IotaDocument; + +#[tokio::main] +async fn main() -> anyhow::Result<()>{ + let storage = get_memstorage()?; + let identity_client = get_client_and_create_account(&storage).await?; + // create new DID document and publish it + let (document, _) = create_did_document(&identity_client, &storage).await?; + + let did = document.id().clone(); + + // We can resolve a `IotaDID` to bytes via client. + // Resolve the associated `Identity Object` and extract the DID document from it. + let client_document: IotaDocument = identity_client.resolve_did(&did).await?; + println!("Client resolved DID Document: {client_document:#}"); +} +``` + +</TabItem> +</Tabs> + +## Advanced Resolver Configuration + +You can configure the `Resolver` to support many use cases by attaching custom resolution handlers. +This enables the `Resolver` to resolve multiple DID methods, as well as customizing how +a particular DID method (such as the IOTA method) gets resolved. + +This feature is mainly intended to be used together with the Resolver's convenience methods for +handling [verifiable presentations](../verifiable-presentations/create-and-validate.mdx) +and [credentials](./../../explanations/verifiable-credentials.mdx). + +### Resolving Multiple DID Methods + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/1_advanced/5_custom_resolution.rs +``` + +</TabItem> +</Tabs> + +## Resolution for Verifiable Presentations + +When validating [verifiable presentations](./../verifiable-presentations/create-and-validate.mdx), you need to +resolve the DID Documents of the [verifiable credential](./../../explanations/verifiable-credentials.mdx) issuers +and presentation holder to verify their signatures. + +Resolving the necessary DID Documents is +[performed automatically when verifying presentations via the `Resolver`](../verifiable-presentations/create-and-validate.mdx#example-code) + +When direct access to these DID Documents is desired, the `Resolver` also provides standalone methods to: + +- Resolve a presentation holder's DID Document. +- Resolve the DID Documents of the issuers of the credentials in a verifiable presentation. +- Resolve the issuer's DID Document for a given verifiable credential. diff --git a/docs/content/iota-identity/how-tos/decentralized-identifiers/update.mdx b/docs/content/iota-identity/how-tos/decentralized-identifiers/update.mdx new file mode 100644 index 00000000000..e1c50866955 --- /dev/null +++ b/docs/content/iota-identity/how-tos/decentralized-identifiers/update.mdx @@ -0,0 +1,380 @@ +--- +sidebar_label: Update +description: How DID Documents can be manipulated and how updates should be published +image: /img/identity/icon.png +tags: + - how-to + - identity + - did +--- + + +# Update DID Documents + +You can extend DID Documents by adding [verification methods](#verification-methods), [services](#services) and custom properties. +A verification method adds public keys, which can be used to digitally sign things like a DID message or a verifiable credential, +while a service can provide metadata around the identity via URIs. + +## Verification Methods + +As demonstrated by the [example](#full-example-code) below, the IOTA Identity framework offers easy-to-use methods for adding +[verification methods](https://www.w3.org/TR/did-core/#verification-methods). + +### Properties + +You can specify the following properties for a verification method: + +- **id**: A [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) for the verification method. You can specify it by setting the [fragment](https://www.w3.org/TR/did-core/#fragment). +- **type**: Specifies the type of the verification method. The framework supports `Ed25519` and `X25519` key types. This property is automatically filled by the framework when specifying the verification material. +- **publicKeyMultibase**: A multi-base encoded public key which concludes the [verification material](https://www.w3.org/TR/did-core/#verification-material). This can be automatically generated by the framework or manually provided by users. + +## Verification Relationships + +[Verification relationships](https://www.w3.org/TR/did-core/#verification-relationships) express the relationship between the DID subject and the verification method. +You can use it to specify the purpose of the verification method. + +### Relationships + +The Identity Framework supports the following relationships: + +- **[Authentication](https://www.w3.org/TR/did-core/#authentication)**: Used to specify authentication methods for the DID subject. +- **[Assertion](https://www.w3.org/TR/did-core/#assertion)**: Used to verify verifiable credentials. +- **[Key Agreement](https://www.w3.org/TR/did-core/#assertion)**: Used for establishing secure communication channels. +- **[Capability Invocation](https://www.w3.org/TR/did-core/#capability-invocation)**: Can be used to authorize updates to the DID Document. +- **[Capability Delegation](https://www.w3.org/TR/did-core/#capability-delegation)**: A mechanism to delegate cryptographic capability to another party. + +Verification methods can be either [embedded or referenced](https://www.w3.org/TR/did-core/#referring-to-verification-methods). Referencing verification +methods allows them to be used by more than one verification relationship. +When you create a verification method using the Identity Framework, specifying the `MethodScope` option will result in an embedded verification method. +Leaving that option unset will create the verification method as a map entry of the `verificationMethod` property. +You can also add verification relationships afterward using references. + +:::note + +Updates to the DID Document are done through the invocation of `Identity::execute_update` API by an `Identity`'s controller. +The public key or address of the controller does not need to be a verification method in the DID Document, +since the controller is authenticated through possession of a `ControllerCap` token. + +::: + +## Services + +[Services](https://www.w3.org/TR/did-core/#services) allow you to add other ways of communicating with the DID subject. +An endpoint included in the DID Document can offer a way of reaching services for different purposes +like authentication, communicating, and discovery. + +### Properties + +You can specify the following properties for a service: + +- **id**: A [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) for referencing the service in the DID document. You can specify it by setting the [fragment](https://www.w3.org/TR/did-core/#fragment). +- **type**: A string used to maximize interoperability between services. The framework does not perform any checks on the content of this string. +- **serviceEndpoint**: A URL that points to the service endpoint. + +## Create Identity + +Before you can update anything, you will need to [create an Identity](./create.mdx). + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust + // Create a new client to interact with the IOTA ledger. + // create new client to interact with chain and get funded account with keys + let storage = get_memstorage()?; + let identity_client = get_client_and_create_account(&storage).await?; + // create new DID document and publish it + let (document, vm_fragment_1) = create_did_document(&identity_client, &storage).await?; + let did: IotaDID = document.id().clone(); +``` + +</TabItem> +</Tabs> + +This creates and publishes an Identity object containing a DID Document with one verification method. + +```json +{ + "doc": { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "verificationMethod": [ + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", + "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", + "crv": "Ed25519", + "x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY" + } + } + ] + }, + "meta": { + "created": "2023-11-16T20:40:03Z", + "updated": "2023-11-16T20:40:03Z", + } +} +``` + +## Add a Verification Method + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust + // Insert a new Ed25519 verification method in the DID document. + let fragment_2: String = document + .generate_method( + &storage, + JwkMemStore::ED25519_KEY_TYPE, + JwsAlgorithm::EdDSA, + None, + MethodScope::VerificationMethod, + ) + .await?; +``` + +</TabItem> +</Tabs> + +This creates a new verification method that includes a newly generated Ed25519 public key. + +```json +{ + "doc": { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "verificationMethod": [ + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", + "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", + "crv": "Ed25519", + "x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY" + } + }, + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", + "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", + "crv": "Ed25519", + "x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo" + } + } + ] + }, + "meta": { + "created": "2023-11-16T20:40:03Z", + "updated": "2023-11-16T20:40:03Z", + } +} +``` + +## Add Verification Relationships + +You can attach verification relationships to a verification method by referencing its fragment. + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust +// Attach a new method relationship to the inserted method. +document.attach_method_relationship( + &document.id().to_url().join(format!("#{fragment_2}"))?, + MethodRelationship::Authentication, +)?; +``` + +</TabItem> +</Tabs> + +This will add `Authentication` relationship to the verification method with the fragment `key-1`. +Note that `Authentication` references the already included `key-2` verification method: + +```json {12,19} +{ + "doc": { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "verificationMethod": [ + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", + "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", + "crv": "Ed25519", + "x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY" + } + }, + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", + "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", + "crv": "Ed25519", + "x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo" + } + } + ], + "authentication": [ + "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE" + ] + }, + "meta": { + "created": "2023-11-16T20:40:03Z", + "updated": "2023-11-16T20:40:03Z", + } +} +``` + +## Add a Service + +You can also add custom properties can to a service by setting `properties`: + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust + // Add a new Service. + let service: Service = Service::from_json_value(json!({ + "id": document.id().to_url().join("#linked-domain")?, + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + }))?; + assert!(document.insert_service(service).is_ok()); + document.metadata.updated = Some(Timestamp::now_utc()); +``` + +</TabItem> +</Tabs> + +The updated Document with the newly created service looks as follows. + +```json {21-27} +{ + "doc": { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "verificationMethod": [ + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", + "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "HZ11e0XacuODQw5FcoMHtcdxl8oXHbSnIhQMUgVzWBE", + "crv": "Ed25519", + "x": "475CGLtezvySFMCHhx6hE9S97MIYMLb4B-pbVEHaCtY" + } + }, + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", + "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", + "crv": "Ed25519", + "x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo" + } + } + ], + "authentication": [ + "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE" + ], + "service": [ + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + } + ] + }, + "meta": { + "created": "2023-11-16T20:40:03Z", + "updated": "2023-11-16T20:40:08Z", + } +} +``` + +## Remove a Verification Method + +You can also remove verification methods at any time using the following snippet: + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust +// Remove a verification method. +let original_method: DIDUrl = document.resolve_method(fragment_1.as_str(), None).unwrap().id().clone(); +document.purge_method(&storage, &original_method).await.unwrap(); +``` + +</TabItem> +</Tabs> + +This removes the original verification method with the fragment `key-1`. + +```json +{ + "doc": { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "verificationMethod": [ + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", + "controller": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE", + "crv": "Ed25519", + "x": "h8ndZ4_Urmzf4xN4emqS8r5q4pAQvAh0k2YHq5JLBBo" + } + } + ], + "authentication": [ + "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#yJz-sPlCmd432JKqK_hkiPml2kj22Jv0aAFy_2jJ8nE" + ], + "service": [ + { + "id": "did:iota:tst:0x19ed80fbd2a644fc2347e27e46e09d42b89df9b1ba09ae41832a9d47d686776a#linked-domain", + "type": "LinkedDomains", + "serviceEndpoint": "https://iota.org/" + } + ] + }, + "meta": { + "created": "2023-11-16T20:40:03Z", + "updated": "2023-11-16T20:40:08Z", + } +} +``` + +## Full Example Code + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/0_basic/1_update_did.rs +``` + +</TabItem> +</Tabs> diff --git a/docs/content/iota-identity/how-tos/domain-linkage/create-and-verify.mdx b/docs/content/iota-identity/how-tos/domain-linkage/create-and-verify.mdx new file mode 100644 index 00000000000..34b437cda69 --- /dev/null +++ b/docs/content/iota-identity/how-tos/domain-linkage/create-and-verify.mdx @@ -0,0 +1,167 @@ +--- +description: How to link a domain and a DID +sidebar_label: Create and Verify +image: /img/identity/icon.png +tags: + - how-to + - identity +--- + +# Domain Linkage + +:::info +To use Domain Linkage in Rust you have to enable the `domain-linkage` feature. +::: + +## Overview + +Domain Linkage can provide proof for a connection between a DID and a domain being controlled by the same entity. +This linkage can transfer trust from a domain to a DID and vice versa. +For instance, if an entity trusts a domain, it can also trust the linked DID and all documents signed by +the verification methods included in the DID Document. + +A use case could be a verifier that trusts `www.example.com`, and receives a verifiable presentation issued by `did:foo:abc`. +If `did:foo:abc` is linked to `www.example.com`, the verifier can trust that the verifiable presentation is issued by +the same entity controlling `www.example.com`. +The DIF has approved a [Well Known DID Configuration](https://identity.foundation/.well-known/resources/did-configuration/) draft to standardize this connection by introducing +the [DID Configuration Resource](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource) and the [Linked Domain Service Endpoint](https://identity.foundation/.well-known/resources/did-configuration/#linked-domain-service-endpoint). + +![Identity getting started](/img/iota-identity/domain-linkage-diagram.png) + +### DID Configuration Resource + +Suppose that a DID `did:foo:example` with the following DID Document only contains one `verificationMethod`, `key-1`: + +```json {5} +{ + "id": "did:foo:abc", + "verificationMethod": [ + { + "id": "did:foo:abc#key-1", + "controller": "did:foo:abc", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "zDShpHKXkcHKHcF8CnGAA1UqyyuEPRNz1XFEuggbWJQSq" + } + ] + }, +``` + +The domain `https://www.example.com` represents the same entity and needs to be linked to increase trust in the DID. + +To establish this link, you must create a [DID Configuration Resource](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource), +and make it available on the [DID Configuration URL](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-uri). +In this case it's `https://example.com/.well-known/did-configuration.json`. + +The [DID Configuration Resource](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource) is a JSON-LD object containing verifiable credentials called `Domain Linkage Credentials`. +Each credential represents a linkage to a single DID. + +:::note + +Note that one `DID Configuration Resource` can include multiple `Domain Linkage Credentials`, +effectively linking the same domain to multiple DIDs. + +::: + +In this example, the domain `https://www.example.com` needs to be linked to the DID `did:foo:abc`. +This means that the `DID Configuration Resource` will have one `Domain Linkage Credential`. +This credential must have the following properties: + +- Its `type` includes `DomainLinkageCredential`. +- It includes the DID Configuration context. +- The `credentialSubject` must be the DID `did:foo:abc` and references the domain `https://www.example.com`. +- The issuer is the DID itself `did:foo:abc`. +- It is signed by a key material included in the DID Document, in this case `did:foo:abc#key-1`. + +```json +{ + "@context": "https://identity.foundation/.well-known/did-configuration/v1", + "linked_dids": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/.well-known/did-configuration/v1" + ], + "type": ["VerifiableCredential", "DomainLinkageCredential"], + "credentialSubject": { + "id": "did:foo:abc", + "origin": "https://www.example.com/" + }, + "issuer": "did:foo:abc", + "issuanceDate": "2023-02-09T22:14:15Z", + "expirationDate": "2024-02-09T22:14:15Z", + "proof": { + "type": "JcsEd25519Signature2020", + "verificationMethod": "did:foo:abc#key-1", + "signatureValue": "4SvYqo3YoArfW7r7qKfN7RUJdZnBteb166KE4UkX8MNdbp5UW6YbykneAzvjyRmf5EVQ9bnP9cS5sbEPUn2uaAcB" + } + } + ] +} +``` + +Now this `DID Configuration Resource` must be made available on `https://example.com/.well-known/did-configuration.json`, +which establishes the linkage. + +### Linked Domain Service Endpoint + +By having a domain, one can discover what DIDs are linked to it by fetching the `DID Configuration Resource` and +investigating the `Domain Linkage Credentials`. + +If you want to enable discovery from the other direction, that is, if you have a DID and want to discover which +domains are linked to it, you can add a [Linked Domain Service Endpoint](https://identity.foundation/.well-known/resources/did-configuration/#linked-domain-service-endpoint) to the DID Document. +The DID Document from this example will be extended as follows to enable discovery of `https://www.example.com`: + +```json {11-17} +{ + "id": "did:foo:abc", + "verificationMethod": [ + { + "id": "did:foo:abc#key-1", + "controller": "did:foo:abc", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "zDShpHKXkcHKHcF8CnGAA1UqyyuEPRNz1XFEuggbWJQSq" + } + ], + "service": [ + { + "id": "did:foo:abc#domain-linkage", + "type": "LinkedDomains", + "serviceEndpoint": "https://www.example.com/" + } + ] +} +``` + +:::note +Note that a DID Document can have multiple `Linked Domain Services` and each service can link to multiple domains. +::: + +### Verifying a DID and Domain Linkage + +As mentioned above, you can discover the Domain Linkage from either direction. +However, verifying the linkage in both cases involves only verifying the DID Configuration Resource. +The process is as follows: + +1. Fetch `DID Configuration Resource` from `https://www.example.com/.well-known/did-configuration.json`. +2. Resolve the DID Document of `did:foo:abc`. +3. Verify the `DID Configuration Resource` and its `Domain Linkage Credential` that references `did:foo:abc`. + + +:::tip About DID Configuration Resource Verification + +You can learn more +[about DID Configuration Resource Verification on the Identity Foundation website](https://identity.foundation/.well-known/resources/did-configuration/#did-configuration-resource-verification). + +::: + +## Example Code + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/1_advanced/6_domain_linkage.rs +``` + +</TabItem> +</Tabs> diff --git a/docs/content/iota-identity/how-tos/key-storage.mdx b/docs/content/iota-identity/how-tos/key-storage.mdx new file mode 100644 index 00000000000..2116166089c --- /dev/null +++ b/docs/content/iota-identity/how-tos/key-storage.mdx @@ -0,0 +1,133 @@ +--- +title: Key Storage +sidebar_label: Key Storage +description: Explain the use of the storage interfaces and how they can be implemented +image: /img/identity/icon.png +tags: + - how-to + - identity +--- + + +## Introduction + +The `JwkDocumentExt` API allows you to modify a DID document, for example, adding new verification methods. +It enables storing the secrets that verification methods represent securely. +It does so using the two storage interfaces, the `JwkStorage` and `KeyIdStorage`. +We refer to both of these as the **key storage**. + +The main idea behind the key storage is strongly inspired by the architecture of key management systems (KMS) +or secure enclaves: once private keys are entered into the system, they can never be retrieved again. +Instead, all operations using the key will have to go through that system. + +This approach allows the key storage to be architected more securely than simply storing and loading private keys from +a regular database. +Of course, security is directly dependent on the concrete implementation, +which is why we provide [Stronghold](https://github.com/iotaledger/stronghold.rs/), a best-effort in-software enclave, by default. + +However, there are cases where one cannot use `Stronghold`, +or may want to integrate key management of identities into their own KMS or similar, +which is why the key storage is an abstraction over such systems. +Any implementation of a key storage can be used by the `JwkDocumentExt` API. + +The two interfaces making up the key storage have two respective responsibilities. + +:::info + +Even though there are two separate interfaces, you can implement them using the same backing storage. + +::: + +## Function Overview + +A brief overview of those functions: + +- `JwkStorage`: CRUD and signing operations on [JSON Web Keys](https://www.rfc-editor.org/rfc/rfc7517). + - `generate`: Generate a new key represented as a JSON Web Key. + - `insert`: Insert an existing JSON Web Key into the storage. + - `sign`: Signs the provided data using the stored private key. + - `delete`: Permanently deletes a key. + - `exists`: Returns whether a key exists. +- `KeyIdStorage`: Stores the mappings from verification methods to their corresponding key identifier in the `JwkStorage`. + - `insert_key_id`: Inserts a mapping from a verification method identifier to a key identifier. + - `get_key_id`: Returns the key identifier for a given verification method identifier. + - `delete_key_id`: Deletes a mapping. + +## Key Identifier + +A `JwkStorage` stores and operates on keys, so they must be identified. +In general, Key Management Systems use some form of an identifier for their keys. +To abstract over those, the `JwkStorage` interface has a general-purpose `KeyId` type, +which is effectively a wrapper around a string. + +A `KeyIdStorage` is needed to store the key id that represents the private key for a given verification method. +To that end, a verification method itself must be identified. + +While within a document, each fragment must be unique, the same is not true given multiple documents, +so we cannot rely only on fragments if we don't want to partition the `KeyIdStorage` by DID. +The solution to this is using a `MethodDigest`, a hash over a verification method. + +When following best security practices, each verification method has its own associated key and, thus, a unique public key. +That, plus the fragment of a method, ensures the `MethodDigest` is unique. + +So, in essence, a `JwkStorage` stores a `KeyId -> JWK` mapping while a `KeyIdStorage` stores a `MethodDigest -> KeyId` mapping. + +:::caution + +Given the construction and limitations of the method digest, +no two documents should contain a method that shares both the same fragment and public key. +This should not happen under typical circumstances, but it is good to keep it in mind. + +::: + +## Key Types + +To express what key types a given `JwkStorage` implementation supports, you should use the `KeyType`, +which is another simple wrapper around a string. + +The reason for this design might seem odd in Rust, given the existence of associated types. +This more simplistic design is necessary to accommodate implementing the interface via the bindings to the library. + +Implementations are expected to export constants of the key types they support, +so users have an easy way to discover the supported types. +In general, storage implementations are free to support any [JSON Web Algorithm](https://www.rfc-editor.org/rfc/rfc7518.html)-compatible key. +However, the recommended default used by IOTA Identity is the `EdDSA` algorithm with curve `Ed25519`. + +## Implementation + +The IOTA Identity library ships two implementations of key storage. +The `JwkMemStore` and `KeyIdMemstore` are insecure in-memory implementations +intended as example implementations and for testing. + +The default key storage implementation is `Stronghold`, +which is an example of a storage that implements both storage interfaces simultaneously. +[`Stronghold`](https://github.com/iotaledger/stronghold.rs/) may be interesting for implementers to look at, +as it needs to deal with some challenges the in-memory version does not have to deal with. Note that the `Stronghold` implementation is only available in Rust. + +## Examples + +This section shows the Rust `Memstore` implementation. + +### `JwkMemStore` + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/identity_storage/src/key_storage/memstore.rs +``` + +</TabItem> +</Tabs> + +### `KeyIdMemstore` + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/identity_storage/src/key_id_storage/memstore.rs +``` + +</TabItem> +</Tabs> diff --git a/docs/content/iota-identity/how-tos/verifiable-credentials/create.mdx b/docs/content/iota-identity/how-tos/verifiable-credentials/create.mdx new file mode 100644 index 00000000000..3862af479cb --- /dev/null +++ b/docs/content/iota-identity/how-tos/verifiable-credentials/create.mdx @@ -0,0 +1,117 @@ +--- +title: Create a Verifiable Credential +sidebar_label: Create and Sign +description: Explain how a VC is created and verified +image: /img/identity/icon.png +tags: + - how-to + - identity + - verifiable-credentials +--- + +A [Verifiable Credential (VC)](./../../explanations/verifiable-credentials.mdx) can represent all +information that a physical credential represents, such as a passport or university +degree. However, by allowing other parties to cryptographically verify the authorship +and integrity of the claims, verifiable credentials can be seen as more tamper-evident +and more trustworthy than their physical counterparts. + +## Verifiable Credential Properties + +In the IOTA Identity Framework, you can create a Verifiable Credential with the following properties: + +- [**Context**](https://www.w3.org/TR/vc-data-model/#contexts): List of JSON-LD context URIs. Includes `"https://www.w3.org/2018/credentials/v1"` by default. +- [**Types**](https://www.w3.org/TR/vc-data-model/#types): List of types describing the credential. Includes `"VerifiableCredential"` by default. +- [**Subject**](https://www.w3.org/TR/vc-data-model/#credential-subject): The issuer's claims; a set of objects that contain one or more properties that are each related to a subject. +- [**Issuer**](https://www.w3.org/TR/vc-data-model/#issuer): The identifier of the issuer, typically their [DID](../../explanations/decentralized-identifiers.mdx). +- [**ID**](https://www.w3.org/TR/vc-data-model/#identifiers): Optional URI identifier for the credential. +- [**Issuance Date**](https://www.w3.org/TR/vc-data-model/#issuance-date): Timestamp for expressing the date and time when a credential becomes valid. +- [**Expiration Date**](https://www.w3.org/TR/vc-data-model/#expiration): Optional timestamp for expressing the date and time when a credential ceases to be valid. +- [**Status**](https://www.w3.org/TR/vc-data-model/#status): Optional information used to determine the current status of a credential, i.e. whether or not it has been [revoked](./revocation.mdx). +- [**Schema**](https://www.w3.org/TR/vc-data-model/#data-schemas): Optional list of objects specifying the schema that the data must conform to. +- [**Refresh Service**](https://www.w3.org/TR/vc-data-model/#refreshing): Optional link to a service where the recipient may refresh the included credentials. +- [**Terms of Use**](https://www.w3.org/TR/vc-data-model/#terms-of-use): Optional list of policies defining obligations, prohibitions, or permissions of the presentation recipient. +- [**Evidence**](https://www.w3.org/TR/vc-data-model/#evidence): Optional list of objects that can be used by the issuer to provide the verifier with additional supporting information in a verifiable credential. +- [**Non-Transferable**](https://www.w3.org/TR/vc-data-model/#nontransferable-property): Optional flag that indicates that a verifiable credential must only be encapsulated in a [verifiable presentation](./../../explanations/verifiable-presentations.mdx) whose proof was issued by the credential subject. + + + + +## Signing + +After preparing the verifiable credential, the issuer creates a signed JWT containing VC in the claims using one of their private keys. This is what allows verifiers to validate the credential independently using the corresponding public key from the issuer's DID Document. + +## Validation + +Verifiers should ensure certain credential properties are valid when receiving one or more in a [verifiable presentation](./../../explanations/verifiable-presentations.mdx). Both issuers and holders may also wish to validate their credentials, particularly directly after creating or receiving one. Validation may be performed at any point in time and can be a useful way of checking whether a credential has expired or been revoked. + +### Validation Checks + +The IOTA Identity Framework supports the following checks during credential validation: + +- **Semantic structure**: Ensures the credential adheres to the specification. +- **Signature**: Verifies the JWS against the issuer's DID Document. +- **Optional validations**: Additional checks on credential properties, and the signature can be configured by specifying [Validation Options](#validation-options). + +### Validation Options + +These options specify conditions that specific properties in a credential must satisfy. + +- **Expiration Date**: Check that the [`expirationDate`](https://www.w3.org/TR/vc-data-model/#expiration) property, if present, is not before a specific date-time. Defaults to the current datetime if unset. +- **Issuance Date**: Check that [`issuanceDate`](https://www.w3.org/TR/vc-data-model/#issuance-date) property, if present, is not after a specific date-time. Defaults to the current datetime if unset. +- **Verifier Options**: Validates aspects of the credential signature. + +## Sharing Verifiable Credentials + +A [verifiable presentation](./../../explanations/verifiable-presentations.mdx) is the recommended data format for sharing one or more verifiable credentials, +as it provides cryptographic means of proving the DID of the holder presenting them, +and for enforcing [subject-holder relationships](https://www.w3.org/TR/vc-data-model/#subject-holder-relationships). + +## Example + +The following code showcases how an issuer can [create, sign](#create-and-sign-a-vc), +and [validate](#validate-a-vc) a verifiable credential. +In this example, the issuer signs a `UniversityDegreeCredential` with Alice's name and DID. + +### Create and Sign a VC + + +<div className={'hide-code-block-extras'}> +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/0_basic/5_create_vc.rs#L36-L74 +``` + +</TabItem> +</Tabs> +</div> + +### Validate a VC + +<div className={'hide-code-block-extras'}> +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/0_basic/5_create_vc.rs#L80-L88 +``` + +</TabItem> +</Tabs> +</div> + +This Verifiable Credential can be [verified by anyone](./../../explanations/verifiable-presentations.mdx), +allowing Alice to take control of it and share it with anyone. + +### Full Example Code + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/0_basic/5_create_vc.rs +``` + +</TabItem> +</Tabs> diff --git a/docs/content/iota-identity/how-tos/verifiable-credentials/revocation.mdx b/docs/content/iota-identity/how-tos/verifiable-credentials/revocation.mdx new file mode 100644 index 00000000000..05e69cfdc62 --- /dev/null +++ b/docs/content/iota-identity/how-tos/verifiable-credentials/revocation.mdx @@ -0,0 +1,174 @@ +--- +sidebar_label: Revoke +description: Explain how a VC can be revoked +image: /img/identity/icon.png +tags: + - how-to + - identity + - verifiable-credentials +--- + + +# Revoke a Verifiable Credential + +The [example](#full-example-code) below demonstrates two methods that an issuer can use to revoke a verifiable credential +using the IOTA Identity Framework: + +1. By using the [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status) field in a credential and linking + to a [revocation method](#revocation-methods). +2. By [removing the verification method](#removing-the-verification-method) that signed the credential. + This invalidates all credentials that were signed with that verification method. + +## Revocation methods + +The IOTA Identity Framework supports two different revocation methods: `RevocationBitmap2022` and `StatusList2021`. + +### Revocation Bitmap + +[RevocationBitmap2022](../../../references/iota-identity/revocation-bitmap-2022.mdx) is the default credential revocation method used in the IOTA Identity Framework. It allows +issuers to control whether a credential is _valid_ or _revoked_. To do so, a revocation list (represented +as a bitmap) is stored in the issuer's DID document. +When a credential is issued, a unique index from the issuer's revocation list +is chosen, linking the credential's status to the value of the list entry. To change the status of a credential, the issuer +simply updates the corresponding entry in its revocation list. + +With `RevocationBitmap2022` the `identity.rs` library completely handles the processes required to handle credentials revocation; +from creation and storage of the revocation list to its lookup. +This makes `RevocationBitmap2022` the preferred way for users to handle credential revocation, but it requires sufficient +funds to rent out the required on-tangle space. + +:::note + +DLT's size constraints limit the size of the revocation list. With the assumption of only one such revocation list +per the issuer's DID document, one may expect to be able to handle roughly 50k entries. + +::: + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/0_basic/7_revoke_vc.rs#L132 +``` + +</TabItem> +</Tabs> + +If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, +if it is 0 (zero) it is not revoked. + +For example, with this approach the issuer adds an index to a credential in the `credentialStatus` field, such as `"5"`. +This part of the credential might then look like this: + +```json +"credentialStatus": { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", + "type": "RevocationBitmap2022", + "revocationBitmapIndex": "5" +}, +``` + +The verifier uses the `id` field (`did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation`) to look up the +service in the issuer's DID document: + +```json +{ + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", + "type": "RevocationBitmap2022", + "serviceEndpoint": "data:application/octet-stream;base64,ZUp5ek1tQmdZR1NBQUFFZ1ptVUFBQWZPQUlF" +} +``` + +During verification, the verifier decodes the revocation bitmap embedded in the `data` URL. +This bitmap written as a bitstring looks like this: `000001`. +Here, the 5th bit is set, which means the credential with that index is revoked, +while all other credentials aren't revoked. + +### StatusList2021 + +[StatusList2021](https://www.w3.org/TR/2023/WD-vc-status-list-20230427) offers similar functionalities to `RevocationBitmap2022` +but in a more flexible and scalable way. +The main difference is that `StatusList2021` is completely agnostic in regards to how the issuer's status list +is stored and fetched, as long as its location can be encoded through a URL. For instance, the status list +can be made available over HTTP (e.g. `https://example.com/credentials/status`) or through the +Interplanetary File System (e.g. `ipfs://QmXDWGdVBhbDoXXzKNMhJk5ejnZgxpMBVzW4EhQaHPD3Mi`). + +This flexibility, although it requires the issuer to manually fetch and update its status list, allows for an arbitrary number of +entries in the status list, in contrast with `RevocationBitmap2022`, where the length of the list is limited by the DLT's constraints. + +Furthermore, `StatusList2021` introduces a new credential state: _suspended_. Suspended credentials are credentials that a validator will not accept as _valid_, but that might become valid again in the future. Instead, _revoked_ credentials **cannot** ever +be valid again, as the _revoked_ state is irreversible. + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/1_advanced/8_status_list_2021.rs#L57-L61 +``` + +</TabItem> +</Tabs> + +First, an issuer creates a credential that encodes a certain status list, specifying its purpose (either `revocation` or `suspension`) +and the location at which it will be available (`https://example.com/credentials/status` in this case). After creation, the issuer +must make the credential available at the chosen URL so that verifiers can fetch it. + +Upon issuing a credential, to revoke it or suspend it later, the issuer sets the `credentialStatus` field, linking +to an entry in its status list. The snippet below shows what `credentialStatus` would look like when linking to the previously created +status list credential. + +```json +{ + "id": "https://example.com/credentials/status#94567", + "type": "StatusList2021Entry", + "statusPurpose": "revocation", + "statusListIndex": "94567", + "statusListCredential": "https://example.com/credentials/status" +} +``` + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/1_advanced/8_status_list_2021.rs#L144 +``` + +</TabItem> +</Tabs> + +To set the status of a credential, the issuer retrieves the status list credential and sets the value of the chosen entry index. + +## Removing the Verification Method + +A less efficient alternative is to remove the verification method that signed the credential from the DID Document of +the issuer. +This means the VC can no longer be validated. + +However, this will also invalidate every VC signed with that verification method, +meaning that the issuer will have to sign every VC with a different key to retain +precise control over which credential is revoked. + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/0_basic/7_revoke_vc.rs#L159-L166 +``` + +</TabItem> +</Tabs> + +## Full Example Code + +The following code exemplifies how you can revoke a [Verifiable Credential (VC)](../../explanations/verifiable-credentials.mdx). + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/0_basic/7_revoke_vc.rs +``` + +</TabItem> +</Tabs> diff --git a/docs/content/iota-identity/how-tos/verifiable-credentials/selective-disclosure.mdx b/docs/content/iota-identity/how-tos/verifiable-credentials/selective-disclosure.mdx new file mode 100644 index 00000000000..9236270d117 --- /dev/null +++ b/docs/content/iota-identity/how-tos/verifiable-credentials/selective-disclosure.mdx @@ -0,0 +1,131 @@ +--- +sidebar_label: Selective Disclosure +description: Explain VC with selective disclosure. +image: /img/identity/icon.png +tags: + - how-to + - identity + - verifiable-credentials +--- + + +# Selective Disclosure (SD-JWT) + + +Holders of verifiable credentials may prefer to keep all the information contained within the credential private from a verifier. Instead, they may opt only to share a specific subset of the properties included in the VC. The identity library implements the [IETF Specifications](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html), which outlines a mechanism to enable the selective disclosure of individual properties within the JSON object of JWT claims. + +## Concept + +### Issuance + +During the issuance process, the issuer replaces a subset of the fields in a credential with digests of their salted values and creates a signed JWT. Next, JWT, alongside the plain text disclosures and the salt used for digest creation are sent to the holder. + +### Presentation + +At this stage, the holder can selectively choose which fields to disclose to the verifier. The disclosures are sent in plain text, with the JWT containing the digests to the verifier. + +:::note +Only values replaced by digests through the issuer can be selectively disclosed. The holder **can not** conceal values provided in plain text in the JWT claims. +::: + + +### Validation + +With these values and a valid signature, the verifier is able to reconstruct a Verified Credential (VC) that exclusively contains the information the holder intended to disclose. + +## How It Works + +A SD JWT can be constructed from the following JWT claim of an address credential in accordance with the [VC Data Model v1.1](https://www.w3.org/TR/vc-data-model/#json-web-token): + + +```json +{ + "iss": "did:iota:tst:0x899d07a766f93c2af1a19a3f4583ad338fc94c5d84b6afcadf49b197e1cb693e", + "jti": "https://example.com/credentials/3732", + "nbf": 1705925652, + "sub": "did:iota:tst:0x6c045e1f658197b432cfc7c66350b8781dca50f820e9de0fcdf0029b4b384355", + "vc": { + "@context": "https://www.w3.org/2018/credentials/v1", + "credentialSubject": { + "address": { + "country": "DE", + "locality": "Maxstadt", + "postal_code": "12344", + "street_address": "Weidenstraße 22" + }, + "name": "Alice" + }, + "type": [ + "VerifiableCredential", + "AddressCredential" + ] + } +} + +``` + +The issuer makes the values of "locality", "postal_code", and "street_address" selectively disclosable, giving the holder the freedom to select what details of the address to be disclosed and presented. + +```json +{ + "_sd_alg": "sha-256", + "iss": "did:iota:tst:0x899d07a766f93c2af1a19a3f4583ad338fc94c5d84b6afcadf49b197e1cb693e", + "jti": "https://example.com/credentials/3732", + "nbf": 1705925652, + "sub": "did:iota:tst:0x6c045e1f658197b432cfc7c66350b8781dca50f820e9de0fcdf0029b4b384355", + "vc": { + "@context": "https://www.w3.org/2018/credentials/v1", + "credentialSubject": { + "address": { + "_sd": [ + "8Dai0-GMZgkzmdryGzjYufUaRFkiNWzVsJJdWucwu84", + "jemTNaG_wiHauwmwWiWREsirAlr91qugPds4MA8e2xo", + "iakC9Dfe2r9fGnOaAr_pGg1b7CwITBjcwE7-O7WlMnY" + ], + "country": "DE" + }, + "name": "Alice" + }, + "type": [ + "VerifiableCredential", + "AddressCredential" + ] + } +} +``` + +:::note +The digests are contained in the `_sd` property in `address`. This allows both keys and values to be concealed. +::: + +For further details, see the [example](#full-example-code) below and the [sd-jwt-payload crate](https://github.com/iotaledger/sd-jwt-payload). + +## Presentation format + +The SD-JWT is presented in the following [format](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#section-5): + +> `<Issuer-signed JWT>~<Disclosure 1>~<Disclosure 2>~...~<Disclosure N>~<optional KB-JWT>` + +## Key Binding JWT + +When a verifier receives an SD-JWT, it may be desirable to verify that the presenter's identity matches the holder of the Credential. For that purpose, a [Key Binding JWT (KB-JWT)](https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#section-4.3) can be used. + + +- The verifier sends a nonce to the holder. +- The holder creates a JWT containing the nonce and the digest of the issuer-signed JWT and the disclosures 1→N. +- The holder sends the KB-JWT to the verifier as a part of the presentation. +- By verifying the KB-JWT, the verifier ensures the identity of the holder, the integrity of the data, the freshness of the signature, and the intended audience. + + + +## Full Example Code + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/1_advanced/7_sd_jwt.rs +``` + +</TabItem> +</Tabs> diff --git a/docs/content/iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure.mdx b/docs/content/iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure.mdx new file mode 100644 index 00000000000..c18ceb0eac3 --- /dev/null +++ b/docs/content/iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure.mdx @@ -0,0 +1,121 @@ +--- +sidebar_label: Zero Knowledge Selective Disclosure +description: Zero Knowledge selectively disclosable VCs. +image: /img/identity/icon.png +tags: + - how-to + - identity + - verifiable-credentials + - zk +--- + + +# Zero Knowledge Selective Disclosure (ZK-SD-VCs) +ZK-SD-VCs allow holders to verify their VCs without having to disclose the entire VC's claim set to verifiers. +This is done through the creation of a Zero Knowledge Proof (ZKP) that guarantees the integrity and authenticity +of the VC, even when only partially disclosed to the verifier. + +:::note +Although ZK-SD-VCs offer similar functionalities to [SD-JWT VCs](selective-disclosure.mdx) - at least on a high level - they rely on completely different +concepts and security concerns. For a user, the most notable difference is the shifted capability of choosing which fields can +be concealed from a verifier. For ZK-SD-VCs it's the holder that has total control over which parts of the credential can be +undisclosed, whereas for SD-JWT VCs it's the issuer that decides which fields may be concealed by the holder. +::: + +## Concepts +### Issuance +The issuer of a ZK-SD-VC creates the credential, signs it using the [BBS+](https://www.ietf.org/archive/id/draft-irtf-cfrg-bbs-signatures-05.html) signature scheme +and sends both the credential and the signature to the holder. To facilitate this process, the credential is first encoded +as a [JSON Proof Token](https://www.ietf.org/archive/id/draft-ietf-jose-json-proof-token-02.html) (JPT), which is then used as the payload of a +[JSON Web Proof](https://www.ietf.org/archive/id/draft-ietf-jose-json-web-proof-02.html) (JWP) and sent to the holder as JPT. +:::note +JWPs and JPTs can be reasoned about as the Zero Knowledge (ZK) based counterparts of JWSs and JWTs. +::: +In code, this process would look like the following snippet: +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/1_advanced/9_zkp.rs#L95-L122 +``` + +</TabItem> +</Tabs> + + +Note how the VC issuer makes no prescription whatsoever regarding the disclosability of the VC's fields. + +### Holder presentation + +Once the holder receives a presentation challenge from a verifier, they construct a selective disclosure presentation for the requested credential +and send it back for verification. For this process the JWP in possession of the holder undergoes a transformation that allows the holder +to conceal any fields from the credentials claims through the creation of a Zero Knowledge Proof (ZKP) of the issuer's signature and becomes a _presented JWP_. +The proof value depends on the selected [JSON Proof Algorithm](https://www.ietf.org/archive/id/draft-ietf-jose-json-proof-algorithms-02.html) (JPA). + +<Tabs groupId="language" queryString> + +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/1_advanced/9_zkp.rs#L177-L203 +``` + +</TabItem> +</Tabs> + +Here's an example presented JWP in its JPT JSON serialization format where the undisclosed values are replaced by `null`: +``` +{ + "payloads": [ + null, + "IkpheSI", + null, + "NDI" + ], + "issuer": "eyJpc3MiOiJodHRwczovL2lzc3Vlci50bGQiLCJjbGFpbXMiOlsiZmFt + aWx5X25hbWUiLCJnaXZlbl9uYW1lIiwiZW1haWwiLCJhZ2UiXSwidHlwIjoiSlBUIiw + icHJvb2ZfandrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiYWNiSVFpdU + 1zM2k4X3VzekVqSjJ0cFR0Uk00RVUzeXo5MVBINkNkSDJWMCIsInkiOiJfS2N5TGo5d + ldNcHRubUt0bTQ2R3FEejh3Zjc0STVMS2dybDJHekgzblNFIn0sInByZXNlbnRhdGlv + bl9qd2siOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiJvQjFUUHJFX1FKSUw + 2MWZVT09LNURwS2dkOGoyemJaSnRxcElMRFRKWDZJIiwieSI6IjNKcW5ya3VjTG9ia2 + RSdU9xWlhPUDlNTWxiRnllbkZPTHlHbEctRlBBQ00ifSwiYWxnIjoiU1UtRVMyNTYif + Q", + "proof": "LJMiN6caEqShMJ5jPNts8OescqNq5vKSqkfAdSuGJA1GyJyyrfjkpAG0c + DJKZoUgomHu5MzYhTUsa0YRXVBnMB91RjonrnWVsakfXtfm2h7gHxA_8G1wkB09x09k + on2eK9gTv4iKw4GP6Rh02PEIAVAvnhtuiShMnPqVw1tCBdhweWzjyxJbG86J7Y8MDt2 + H9f5hhHIwmSLwXYzCbD37WmvUEQ2_6whgAYB5ugSQN3BjXEviCA__VX3lbhH1RVc27E + YkRHdRgGQwWNtuExKz7OmwH8oWizplEtjWJ5WIlJpee79gQ9HTa2QIOT9bUDvjjkkO- + jK_zuDjZwh5MkrcaQ", + "presentation": "eyJub25jZSI6InVURUIzNzFsMXB6V0psN2FmQjB3aTBIV1VOaz + FMZS1iQ29tRkx4YThLLXMifQ" +} +``` + +### Verification + +The verifier decodes the received JPT presentation and asserts the validity of the ZKP it contains, thus proving the +authenticity and integrity of the presented credential, without knowledge of any of the undisclosed fields and of the issuer signature. + +<Tabs groupId="language" queryString> + +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/1_advanced/9_zkp.rs#L225-L237 +``` + +</TabItem> +</Tabs> + +## Full Example Code + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/tree/v1.6.0-alpha/examples/1_advanced/9_zkp.rs +``` + +</TabItem> +</Tabs> diff --git a/docs/content/iota-identity/how-tos/verifiable-presentations/create-and-validate.mdx b/docs/content/iota-identity/how-tos/verifiable-presentations/create-and-validate.mdx new file mode 100644 index 00000000000..7aff05427ca --- /dev/null +++ b/docs/content/iota-identity/how-tos/verifiable-presentations/create-and-validate.mdx @@ -0,0 +1,107 @@ +--- +sidebar_label: Create and Validate +description: Explain how a VP is created and verified +image: /img/identity/icon.png +tags: + - how-to + - identity +--- + +# Create and Validate Verifiable Presentations + +The IOTA Identity Framework enables holders to easily construct +[verifiable presentations](./../../explanations/verifiable-presentations.mdx). +As demonstrated in the [example](#example-code), +holders only need to pass in their credentials to create a JWT presentation. + +## Properties + +You can specify the following properties in a presentation: + +- [**ID**](https://www.w3.org/TR/vc-data-model/#identifiers): Optional URI identifier for the presentation. +- [**Context**](https://www.w3.org/TR/vc-data-model/#contexts): List of JSON-LD context URIs. Includes `"https://www.w3.org/2018/credentials/v1"` by default. +- [**Types**](https://www.w3.org/TR/vc-data-model/#types): List of types describing the presentation. Includes `"VerifiablePresentation"` by default. +- [**Credentials**](https://www.w3.org/TR/vc-data-model/#dfn-verifiable-credentials): List of verifiable credentials to present. +- [**Holder**](https://www.w3.org/TR/vc-data-model/#dfn-holders): Optional URI, typically a DID, of the entity that generated the presentation. +- [**Refresh Service**](https://www.w3.org/TR/vc-data-model/#refreshing): Optional link to a service where the recipient may refresh the included credentials. +- [**Terms of Use**](https://www.w3.org/TR/vc-data-model/#terms-of-use): Optional list of policies defining obligations, prohibitions, or permissions of the presentation recipient. + +Of the above, **only the list of credentials is required** when creating a presentation using the framework. +However, the holder property should be included to satisfy [subject-holder relationship](#subject-holder-relationship) checks during validation. + +After creation, the holder signs the verifiable presentation using a private key linked to one of the verification +methods in their DID Document and transmits it to a verifier for validation. + +## Creation and Validation + +A Verifiable Presentation can be issued as a JWT that provides data integrity, +and also proves the [DID](../../explanations/decentralized-identifiers.mdx) of the holder. + +:::note + +Verifiers should always send a challenge +to [mitigate replay attacks](./../../explanations/verifiable-presentations.mdx#security-considerations). +::: + + +The IOTA Identity Framework provides several options for verifiers to validate various sections of a verifiable presentation. +See the [example](#example-code) for a demonstration of how to validate a presentation. + +The framework checks: + +- **Semantic structure**: Ensures the presentation and its credentials adhere to the specification. +- **Presentation proof**: Verifies the presentation signature against the holder's DID document. +- **Credential proofs**: Verifies the credential signatures against the DID Documents of their respective issuers. + + +Currently, the following are **not** checked automatically: + +- **Data schemas**: Credentials that specify a [schema](https://www.w3.org/TR/vc-data-model/#data-schemas) property +should be examined to ensure conformance. +- **Fitness for purpose**: Whether the credentials in a presentation and the data within them are acceptable and +valid depends on the context in which they are used. Verifiers should ensure that the credential types, subjects, +and schemas sent by a holder match what was requested. +- **Issuer trustworthiness**: Verifiers must check that they trust the issuer on each individual credential in a +presentation. The framework only verifies that the issuer's signature on each credential is current and valid +against the given options. + +The default validation behavior may be modified by the following options. + +## Subject-Holder Relationship + +Specifies the expected relationship between the holder that signed the verifiable presentation and the subject +specified in each [verifiable credential](./../../explanations/verifiable-credentials.mdx). +This can be restricted by the [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property, +which indicates that a verifiable credential must only be encapsulated into a verifiable presentation whose holder matches the credential subject. + +By default, the framework always enforces that the holder matches the subject. + +The following options are available to modify that behavior: + +- **`AlwaysSubject` (default)**: The holder DID that signed the presentation must match the [`credentialSubject` `id`](https://www.w3.org/TR/vc-data-model/#credential-subject) field in each of the attached credentials. This is the safest option which ensures holders may only present credentials that were directly issued to their DID. An error is thrown on a mismatch or if no subject `id` is present. +- **`SubjectOnNonTransferable`**: The holder DID must match the subject only for credentials where the [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property is `true`. This is appropriate for accepting [bearer credentials](https://www.w3.org/TR/vc-data-model/#bearer-credentials) while still adhering to the specification. +- **`Any`**: The holder DID is not required to have any kind of relationship to any credential subject. This option performs no checks and ignores the [`nonTransferable`](https://www.w3.org/TR/vc-data-model/#nontransferable-property) property. + +:::note + +See the [Verifiable Credentials Data Model Specification](https://www.w3.org/TR/vc-data-model/#subject-holder-relationships) +for further discussion on the different subject-holder relationships. + +::: + + +## Example Code + +The following code demonstrates how to use the IOTA Identity Framework end-to-end to create and sign a verifiable +presentation as a holder, serialize it to JSON for transmission, deserialize it on the receiving side as a verifier, +and finally validate it with various options. + +<Tabs groupId="language" queryString> +<TabItem value="rust" label="Rust"> + +```rust reference +https://github.com/iotaledger/identity.rs/blob/v1.6.0-alpha/examples/0_basic/6_create_vp.rs +``` + +</TabItem> +</Tabs> diff --git a/docs/content/iota-identity/index.mdx b/docs/content/iota-identity/index.mdx new file mode 100644 index 00000000000..80da6184998 --- /dev/null +++ b/docs/content/iota-identity/index.mdx @@ -0,0 +1,79 @@ +--- +description: The most important concepts that developers will need to know to utilize IOTA Identity to its full potential. +image: /img/identity/icon.png +tags: + - reference + - identity +--- +# IOTA Identity Framework + +![IOTA Identity](/img/banner/banner_identity.svg) + +The IOTA Identity framework implements the most common standards and patterns for Decentralized Identity in both a DLT agnostic and `iota` method-specific manner. +It is designed to work for Identity for [People](#identity-for-people), [Organizations](#identity-for-organizations), +[Things, and Objects](#identity-for-things) acting as a unifying layer of trust between everyone and everything. + +## Introduction to Decentralized Identity + +Decentralized or Self-Sovereign Identity (SSI) gives individuals full control over their online identity, +offering a remedy for database breaches, lack of digital trust, and stringent privacy laws like GDPR. +Digital identity bridges the gap between online pseudonyms and real-world personas, enabling true verifiable identities. This gives individuals the power to choose which data to share and with whom. + +### Identity for People + +:::info Privacy + +IOTA Identity builds a new internet, without usernames, passwords, endless repeated forums, or uncontrolled data harvesting. + +::: + +Information about anyone's life is spread across many locations. Most people have numerous unorganized important documents at home, hundreds of online accounts, and many more online footprints. Through statistical predictive analysis, computer programs can harvest unverified online information sources and create a reasonably accurate profile about our lives. These profiles are accurate enough for targeted advertising and personalized content but lack the proof and trust for them to be used in business. This results in an antiquated customer experience where we have to submit our age and address for every purchase we make and every account we create. It also inhibits our ability to do many online tasks like requesting and extending licenses or taking out a mortgage. + +Self-Sovereign Identity is about returning autonomy and privacy to the individual, while also improving our online experience. Some movements focus on data privacy, preventing companies from using our information altogether, but with the IOTA Identity framework you control which part of the information you want to reveal. The user can create a single online profile containing all our personal information. They can decide who they share what information with, and a verifier checks if the information is correct, making the data trustworthy. This moves their online profile from a statistical estimation by corporate entities to an accurate and verifiable profile under their own control. + +IOTA Identity allows a new internet without usernames, passwords, endlessly repeated forms or data harvesting. Users have ultimate control and can choose to supply service providers with their personal data, who in return provide personalized experiences. Data will still flow, and perhaps even more than before, but it will always be in the individual's interest instead of a corporation's. People will gain additional benefits in sharing their data, either in monetary value or improved customer experience. This system is impossible in non-neutral environments such as permissioned or fee-based ledgers. + +Governmental mechanisms for building _digital identities_ are currently being established throughout Europe and Asia, with demand increasing around the globe. However, they are managed by single entities and restricted to the governments that created them. By decentralizing a framework for these standards to adapt to, we have a system for intergovernmental verification of individuals and devices. A person’s digital identification will be transferable across borders like a passport. However, it will no longer require the trust of the issuing government due to the digital trust established by the open and auditable system. + +### Identity for Organizations + +:::info GDPR + +IOTA Identity allows organizations to comply with GDPR in a cost-efficient and privacy-enabling manner + +::: + +Corporations are associated with greed and abuse of power. This reputation stems from the role some have chosen to take within society. Corporations are trusted with our data, but often do not act responsibly; vulnerability, fix, patch, repeat. In software and systems, we have seen this cycle repeat. Headlines on data leaks are now an ever-present feature in the news. + +IOTA Identity presents an opportunity for companies to embrace a new role in the ecosystem. Traditional approaches do not provide cost-efficient solutions to new legislation like GDPR. IOTA Identity enables organizations to change their processes to comply with the new regulations in a cost-efficient and privacy-enabling manner. Features of “Data Protection and Privacy by Design” shift responsibility over Personal Identifiable Information (PII) from organization to customer, and organizations no longer need to store that data. The relationship between customer and organization is also tightened as communication via a third-party Identity provider like Google or Facebook is no longer needed. + +Due to Know-Your-Customer (KYC) and Anti-Money Laundering (AML) obligations, companies can be certain who their customers are. These services also provide unique insight into their customers’ data. These insights can be combined and translated into verifiable credentials, providing a new “Trust Anchor” service with the potential for new business models. KYC and AML credentials would return the autonomy of personal data to the customer. Once companies accept other companies' KYC and AML credentials, the enrollment time for new customers is significantly reduced, as are the costs. With the personal data secured by the customer, companies can afford to store less data in their databases, reducing risk and responsibility and fulfilling the goals of legislation such as GDPR. + +Organizations that have their own decentralized identities can also combat fraud and increase control over their online brand. Companies can sign invoices and agreements using their decentralized identities. While interacting with the customers, they will also be able to reliably identify themselves. + +### Identity for Things + +:::info TRUST + +IOTA Identity adds the missing key ingredient for the "Economy of Things": Trust. + +::: + +With Identity of Things (IDoT), devices are provided with a unique global identity that are able to prove many attributes, including their capabilities, specifications, and authenticity. People, organizations, and other devices will only pay for devices that can prove their ability to fulfill the required task. This basis of trust prevents fraudulent activity. Additionally, using the IOTA ledger, the task's progress can be immutably logged. Combining the IOTA protocol and the IOTA Identity framework, we can automate the entire interaction between all parties without requiring predefined trust. The [Industry Marketplace](https://industry.iota.org/) provides a perfect example of how this framework and level of autonomy work. + +There is a growth in applications that generate Digital Twins for physical devices or objects, such as the Asset Administration Shell (AAS) developed for our Industry Marketplace. Digital twins are online profiles representing a device or object. They provide a virtual state that mirrors reality by emulating the device or object’s physical state through data input sources like sensors. A digital twin is often used to monitor states and execute actions based on the information. Digital twins are only rarely shared outside the associated application and organization due to the complexities in sharing and matching profiles. However, empowered with a digital identity, digital twin sharing would become possible. Once data is verifiable and trusted, digital twins can form the basis for the digital representation of physical devices and objects. This allows other identities to interact with them automatically and provide services such as predictive maintenance. + +Security is a major barrier to advancing technologies that use IoT. Whether it is the smart devices in our homes or at a larger scale, the critical infrastructure of organizations and cities, security must be at the core. It is central to any globally unifying identity solution. By integrating advanced research in cryptography and digital ledgers and combining it with a scalable access and management system, security will become a core functionality of the systems we build. By using scalable device DIDs, integrating verification and reputation schemes, and allowing for transparent tamper-proof accountability, we begin to understand how we can future-proof the security of our systems, allowing us to start trusting the process and not the patch. + +### One Framework. Any Identity + +The IOTA Identity framework serves as a ubiquitous layer of trust for the internet. Whether it's people, organizations, or things, the framework enables the creation of digital identities, fosters trust-building through verifiable credentials, and ensures seamless interaction among different entities. + +### Why IOTA? + +IOTA stands apart as a scalable Distributed Ledger Technology (DLT), suitable for a universal identity solution. Some features of IOTA include: + +* **Cost-effectiveness**: Creating Identities on IOTA incurs minimal fees and deposits for occupied ledger space can be reclaimed at any time. +* **High availability**: Identities are always available on all network nodes - for holders, issuers, and verifiers. +* **Security**: Write access to identities is secured through multi-level control structures with key rotation capabilities, allowing for backup access and recoverability. +* **Integrity**: Updates go through the same mechanisms that secure the IOTA network, guaranteeing consistent state and history of all identities. diff --git a/docs/content/references/iota-identity/iota-did-method-spec.mdx b/docs/content/references/iota-identity/iota-did-method-spec.mdx new file mode 100644 index 00000000000..bdd13a83ac7 --- /dev/null +++ b/docs/content/references/iota-identity/iota-did-method-spec.mdx @@ -0,0 +1,226 @@ +--- +title: IOTA DID Method Specification v2.0 +sidebar_label: DID Method +description: How IOTA Identity implements the Decentralized Identifiers Standard on the IOTA Tangle. +image: /img/identity/icon.png +tags: + - reference + - identity + - did +--- + +# IOTA DID Method Specification v2.0 + +2024-08-23 + +## Abstract + +The IOTA DID Method Specification describes a method of implementing the [Decentralized Identifiers](https://www.w3.org/TR/did-core/) (DID) standard on [IOTA](https://iota.org), a Distributed Ledger Technology (DLT). It conforms to the [DID specification v1.0](https://www.w3.org/TR/did-core/) and describes how to perform Create, Read, Update and Delete (CRUD) operations for IOTA DID Documents using shared Move objects on the IOTA network. + +## Introduction + +{/* +### Move-based DLT + +:::warning +We might want to borrow text for this section from other parts of the wiki that deal with this in more details! +::: +*/} + +### Identity Object + +`Identity` is a **shared** _Move_ object that contains a DID document along other metadata used for access-control. + +Although `Identity`'s instances are **shared**, and therefore publicly accessible by anyone, only a well-defined per-instance set of +actors - called **controllers** - are allowed to make changes to an `Identity`. + +### Ledger and DID + +Storing DID Documents in the ledger state means they inherently benefit from the guarantees the ledger provides. + +1. Conflicts among nodes are resolved and dealt with by the ledger. +2. Replay attacks are mitigated since transactions need to be confirmed by the ledger. +3. Through the `State Index` a linear history for updates of a DID Document is provided. + +## DID Method Name + +The `method-name` to identify this DID method is: `iota`. + +A DID that uses this method MUST begin with the following prefix: `did:iota`. Following the generic DID specification, this string MUST be lowercase. + +## DID Format + +The DIDs that follow this method have the following ABNF syntax. It uses the syntax in [RFC5234](https://www.rfc-editor.org/rfc/rfc5234) and the corresponding definition for `digit`. + +``` +iota-did = "did:iota:" iota-specific-idstring +iota-specific-idstring = [ iota-network ":" ] iota-tag +iota-network = 8lowercase-hex +iota-tag = "0x" 64lowercase-hex +lowercase-hex = digit / "a" / "b" / "c" / "d" / "e" / "f" +``` + +It starts with the string "did:iota:", followed by an optional network name (8 lowercase hexadecimal digits) and a colon, then the tag. +The tag starts with "0x" followed by a lowercase hex-encoded `Object ID`. + +### IOTA-Network + +The iota-network is an identifier of the network where the DID is stored. This network must be an IOTA Ledger, but can either be a public or private network, permissionless or permissioned. + +When no IOTA network is specified, it is assumed that the DID is located on IOTA main network, having id **PUT IOTA CHAIN ID HERE**. This means that the following DIDs will resolve to the same DID Document: + +``` +did:iota:IOTA CHAIN ID:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe +did:iota:0xe4edef97da1257e83cbeb49159cfdd2da6ac971ac447f233f8439cf29376ebfe +``` + +### IOTA-Tag + +An IOTA-tag is a lowercase hex-encoded `Object ID`. The `Object ID` itself is a unique identifier for _Move_ objects and is derived from the digest of the trasanction that created the object it identifies. +This tag identifies the `Identity` where the DID Document is stored, and it will not be known before the generation of the DID since it will be assigned when the `Identity` object is created. + +### Anatomy of the encoded DID Document + +The DID Document stored within an `Identity` must be encoded as the payload of a byte packed structure with fields as follows: + +| Name | Type | Description | +| ------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Document Type | ByteArray[3] | Set to value **DID** to denote a DID Document. | +| Version | uint8 | Set value **1** to denote the version number of this method | +| Encoding | uint8 | Set to value to **0** to denote JSON encoding without compression. | +| Payload | (uint16)ByteArray | A DID Document and its metadata, where every occurrence of the DID in the document is replaced by `did:0:0`. It must be encoded according to `Encoding`. | + +The types are defined in [TIP-21](https://github.com/iotaledger/tips/blob/main/tips/TIP-0021/tip-0021.md). + +#### Payload + +The payload must contain the following fields: + +- `meta`: contains metadata about the DID Document. For example, `created` to indicate the time of + creation, and `updated` to indicate the time of the last update to the document. It may also include other properties. +- `doc`: contains the DID Document. In the example below, the document only contains one verification method. The `id` and `controller` is specified by `did:0:0` which references the DID of the document itself, since the DID is unknown at the time of publishing. It also deduplicates the DID of the document to reduce the size of the state metadata, in turn reducing the required storage deposit. + +Example State Metadata Document: + +```json +{ + "doc": { + "id": "did:0:0", + "verificationMethod": [ + { + "id": "did:0:0#jkGOGVO3Te7ADpvlplr47eP9ucLt41zm", + "controller": "did:0:0", + "type": "JsonWebKey", + "publicKeyJwk": { + "kty": "OKP", + "alg": "EdDSA", + "kid": "jkGOGVO3Te7ADpvlplr47eP9ucLt41zm", + "crv": "Ed25519", + "x": "D5w8vG6tKEnpBAia5J4vNgLID8k0BspHz-cVMBCC3RQ" + } + } + ], + "authentication": ["did:0:0#jkGOGVO3Te7ADpvlplr47eP9ucLt41zm"] + }, + "meta": { + "created": "2023-08-28T14:49:37Z", + "updated": "2023-08-28T14:50:27Z" + } +} +``` + +## Multiple _controllers_ and _voting power_ + +An `Identity` may be owned by more than one controller and each controller may have a different **voting power** - i.e. a quantificable level of control over a given `Identity` represented with an unsigned integer. +Whenever control over an `Identity` is shared among multiple controllers an access control (AC) policy - in the form of an unsigned positive integer **threshold** - must be defined. Any operation a controller performs that might modify the interested `Identity` isn't carried out right away. Instead, a **proposal** to perform such an operation is created, and controllers are required to approve it, before it may be executed. The number of approvals needed in order to execute the proposal depends on the threshold and controllers' voting power. + +## CRUD Operations + +Create, Read, Update and Delete (CRUD) operations that change the DID Documents are performed by invoking `Identity`'s APIs in a transaction. + +**These operations require funds to cover the gas cost. Transactions must be carefully done in order to avoid fund loss** - e.g. executing a transaction that is bound to fail wastefully consumes funds for gas. Additionally, private keys of controllers must be stored securely. + +### Create + +In order to create a simple self controlled DID two things are required: + +1. An Address for which the private key is available. +2. Enough funds to cover for the creation of a new `Identity` object. + +Creation steps: + +1. Create the content of the DID Document like verification methods, services, etc. +2. Create the payload and the headers as described in the [Anatomy of the encoded DID document](#anatomy-of-the-encoded-did-document). +3. Execute `Identity::new` in a transaction, providing the encoded DID document as its parameter. + +Once the transaction is confirmed, the DID is published and can be formatted by using the `Object ID` as the tag in [DID Format](#did-format). + +### Read + +The following steps can be used to read the latest DID Document associated with a DID. + +1. Obtain the `Object ID` from the DID by extracting the `iota-tag` from the DID, see [DID Format](#did-format). +2. Obtain the network of the DID by extracting the `iota-network` from the DID, see [DID Format](#did-format). +3. Query the Object corresponding to the `Object ID` using a node running the indexer, asserting that the network the node is connected to matches the previously extracted network ID. +4. Assert that the `Object ID` of the returned Object matches the `Object ID` extracted from the DID. Return an error otherwise. +5. Parse the retrieved object into an `Identity` object. +6. Decode the DID Document from its byte packed encoding stored in the `Identity` Object. +7. Replace the placeholder `did:0:0` with the DID given as input. + +### Update + +Updating a DID Document can be achieved by invoking the `Identity::propose_update` API passing the updated encoded DID Document as its parameter, followed by executing the generated proposal after enough approvals from the `Identity`'s controllers are obtained. +In a more detailed step-by-step description: + +1. Create a copy of the encoded DID document with the `Object ID` set explicitly. +2. Pack the updated DID Document, as described in the [Anatomy of the encoded DID Document](#anatomy-of-the-encoded-did-document), and use it as the parameter to a call to `Identity::propose_update` API. +3. Query the generated `Proposal` object, and check if the threshold has been reached. If that's not the case wait for enough controllers to approve the proposal. +4. Execute the approved proposal by invoking `Identity::execute_update`. + +### Delete + +#### Deactivate + +Temporarily deactivating a DID can be done by deleting the contents of the encoded DID Document in the `Identity` object, setting it to an empty byte array, and publishing an [update](#update). + +Another option is to [update](#update) the DID Document and set the `deactivated` property in its `metadata` to true. In both cases, the deactivated DID Document will be marked as `deactivated` when resolved. + +#### Destroy + +To permanently destroy a DID Document controllers need to go through a process similar to that of [updating](#update) the document. First, a proposal for destroying the corresponding `Identity` is created, which can be executed after the AC policy has been met. + +Note that this operation irreversibly and irrecoverably deletes the DID. This is because the `Object ID` from which an IOTA DID is derived (see [IOTA-Tag](#iota-tag)) is generated from the hash of the input transaction that created it, which cannot generally be replicated. + +## IOTA Identity standards + +The `did:iota` method is implemented in the [IOTA Identity framework](https://github.com/iotaledger/identity.rs). This framework supports a number of operations that are standardized, some are standardized across the SSI community, and some are the invention of the IOTA Foundation. + +### Revocation + +Revocation of verifiable credentials and signatures can be achieved using the [Revocation Bitmap 2022](./revocation-bitmap-2022.mdx) where issuers store a bitmap of indices in the DID Document. These indices correspond to verifiable credentials they have issued. If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, if it is 0 (zero) it is not revoked. + +### Standardized Services + +The IOTA Identity framework also standardized certain `services` that are embedded in the DID Document. It is RECOMMENDED to implement these when implementing the `did:iota` method. + +Currently standardized `services`: + +- [Revocation Bitmap Service](./revocation-bitmap-2022.mdx#revocation-bitmap-service) + +## Security Considerations + +### Private Key Management + +All private keys or seeds used for the `did:iota` method should be equally well protected by the users. Private keys of the state controller and the governor are especially important as they control how keys are added or removed, providing full control over the identity. + +## Privacy Considerations + +### Personal Identifiable Information + +IOTA networks are immutable. This means that once something is included, it can never be completely removed. For example, destroying an `Identity` will remove it from the ledger state, but it can still be stored in permanodes or by any party that records historical ledger states. + +That directly conflicts with certain privacy laws such as GDPR, which have a 'right-to-be-forgotten' for Personal Identifiable Information (PII). As such, users should NEVER upload any PII, including inside DID Documents. While verifiable credentials can be made public, this should only be utilized by Identity for Organisations and Identity for Things. + +### Correlation Risks + +As with any DID method, identities can be linked if they are used too often and their usage somehow becomes public. See [DID Correlation Risks](https://www.w3.org/TR/did-core/#did-correlation-risks). Additionally, a DID can be correlated with funds if the Alias Output used to store the DID Document or any of its controllers is used for holding, transferring or controlling coins or NFTs. diff --git a/docs/content/references/iota-identity/overview.mdx b/docs/content/references/iota-identity/overview.mdx new file mode 100644 index 00000000000..db1b1da3510 --- /dev/null +++ b/docs/content/references/iota-identity/overview.mdx @@ -0,0 +1,17 @@ +--- +title: Specifications Overview +sidebar_label: Overview +description: Provide overview of the specifications +image: /img/identity/icon.png +tags: + - reference + - identity +--- + +While IOTA Identity implements many existing standards, it also adds some additional features we would like to standardize ourselves. This section covers these features and how they work in great detail. These are not light reads and can be skipped. + +The current specifications are: + +- [IOTA DID](iota-did-method-spec.mdx): The specification for the IOTA DID Method implemented on the IOTA network. +- [Revocation Bitmap 2022](revocation-bitmap-2022.mdx): The specification for an on-chain credential revocation mechanism. +- [Revocation Timeframe 2024](revocation-timeframe-2024.mdx): The specification for a privacy-enhanced on-chain credential revocation mechanism. \ No newline at end of file diff --git a/docs/content/references/iota-identity/revocation-bitmap-2022.mdx b/docs/content/references/iota-identity/revocation-bitmap-2022.mdx new file mode 100644 index 00000000000..0a373d8c5b9 --- /dev/null +++ b/docs/content/references/iota-identity/revocation-bitmap-2022.mdx @@ -0,0 +1,199 @@ +--- +title: Revocation Bitmap +sidebar_label: Revocation Bitmap +description: The specification for the embedded revocation bitmap. +image: /img/identity/icon.png +tags: + - reference + - identity + - did +--- + +# Revocation Bitmap 2022 + +## Abstract + +This specification describes a mechanism for publishing the revocation status of [verifiable credentials](../../iota-identity/explanations/verifiable-credentials.mdx) embedded in an issuer's DID document. + +## Introduction + +Revocation gives an issuer the capability to invalidate a credential they issued before its natural expiration date. To achieve this, issuers can embed an identifier in the `credentialStatus` field of a credential. Verifiers can then lookup that identifier in a separate list, to check whether the credential is still valid. This document specifies a mechanism of embedding such a list, in form of a bitmap, in the DID document of the issuer, where each bitmap index corresponds to a credential they have issued. This mechanism is space-efficient and enables a verifier to check a credential's status in a privacy-preserving manner and without requiring additional lookups or external resources. + +## Revocation Bitmap Concept + +The revocation status of a verifiable credential is expressed as a binary value. The issuer keeps a bitmap of indices corresponding to verifiable credentials they have issued. If the binary value of the index in the bitmap is 1 (one), the verifiable credential is revoked, if it is 0 (zero) it is not revoked. + +## Data Model + +### Revocation Bitmap Status + +For an issuer to enable verifiers to check the status of a verifiable credential, the [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status) property MUST be specified with the following properties: + +| Property | Description | +| :---------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | The constraints on the `id` property are listed in the [Verifiable Credentials Data Model specification](https://www.w3.org/TR/vc-data-model/). The `id` MUST be a [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) that is the URL to a [Revocation Bitmap Service](#revocation-bitmap-service) in the DID Document of the issuer. It SHOULD include an `index` query set to the same value as `revocationBitmapIndex`, to uniquely identify the `credentialStatus`. If the `index` query is present, implementations SHOULD reject statuses where the `index` query's value does not match `revocationBitmapIndex`. | +| `type` | The `type` property MUST be `"RevocationBitmap2022"`. | +| `revocationBitmapIndex` | The `revocationBitmapIndex` property MUST be an unsigned, 32-bit integer expressed as a string. This is the index of the credential in the issuer's revocation bitmap. Each index SHOULD be unique among all credentials linking to the same [Revocation Bitmap Service](#revocation-bitmap-service). | + +#### Example + +An example of a verifiable credential with a `credentialStatus` of type `RevocationBitmap2022`. + +```json +{ + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "credentialSubject": { + "id": "did:iota:B8DucnzULJ9E8cmaReYoePU2b7UKE9WKxyEVov8tQA7H", + "GPA": "4.0", + "degree": "Bachelor of Science and Arts", + "name": "Alice" + }, + "issuer": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "issuanceDate": "2022-06-13T08:04:36Z", + "credentialStatus": { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw?index=5#revocation", + "type": "RevocationBitmap2022", + "revocationBitmapIndex": "5" + }, + "proof": { + "type": "JcsEd25519Signature2020", + "verificationMethod": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#key-1", + "signatureValue": "2eHdbDumMrer4pNVkaiYMqsVqVp2adq7bRcgTJZiw17Zeghk2ZT49YHwLwCCg35YKganBhxP6YSbzYoBK1AuCUv" + } +} +``` + +### Revocation Bitmap Service + +To allow verifiers to check the status of a [Revocation Bitmap Status](#revocation-bitmap-status), the DID document of the credential issuer MUST contain a [service](https://www.w3.org/TR/did-core/#services) with the following properties: + +| Property | Description | +| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | The constraints on the `id` property are listed in the [DID Core service specification](https://www.w3.org/TR/did-core/#services). The `id` property MUST be a DID URL uniquely identifying the revocation bitmap. | +| `type` | The `type` property MUST be `"RevocationBitmap2022"`. | +| `serviceEndpoint` | The `serviceEndpoint` MUST be generated according to the [service endpoint generation algorithm](#service-endpoint-generation-algorithm). | + +#### Example + +An example of an issuer's DID document where credential `"5"` in the `#revocation` service is revoked: + +```json +{ + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "verificationMethod": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#key-1", + "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z3hgM9fNkhwgT5mECbj1HdKoFNZgpffwQYEV8WBVHphXq" + } + ], + "capabilityInvocation": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#sign-0", + "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z83F6zbD3KqaxvQhqo25LvSXzoDdpZmp3EpPVonSVACwZ" + } + ], + "service": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", + "type": "RevocationBitmap2022", + "serviceEndpoint": "data:application/octet-stream;base64,ZUp5ek1tQmdZR1NBQUFFZ1ptVUFBQWZPQUlF" + } + ] +} +``` + +## Algorithms + +The following algorithms define how to generate, expand and validate revocation bitmaps. + +### Service Endpoint Generation Algorithm + +The following process MUST be followed when producing a `RevocationBitmap2022` to embed in a service endpoint: + +1. Let **bitmap** be a [_roaring bitmap_](https://roaringbitmap.org/) where each bit is initialized to 0. +2. For each revoked credential with an **index** not exceeding an unsigned, 32-bit integer, set the corresponding bit in **bitmap** at **index** to 1. +3. Generate the **bitmap serialization** according to the [roaring bitmap serialization format](https://github.com/RoaringBitmap/RoaringFormatSpec/) using the **bitmap** as input. +4. Generate a **compressed bitmap** by using the ZLIB compression algorithm [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)] on the **bitmap serialization** and base64-encoding [[RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)] the result. +5. Create the **service endpoint** by embedding the **compressed bitmap** in a data URL [[RFC2397](https://datatracker.ietf.org/doc/html/rfc2397)]. On the data url, the `<mediatype>` MUST be `application/octet-stream` and the `base64` attribute MUST be set. +6. Return the **service endpoint**. + +### Service Endpoint Expansion Algorithm + +The following process MUST be followed when expanding the endpoint from a service of type `RevocationBitmap2022`: + +1. Let **service endpoint** be a data url generated using the [service endpoint generation algorithm](#service-endpoint-generation-algorithm). +2. The `<mediatype>` of the **service endpoint** MUST be `application/octet-stream` and the `base64` attribute MUST be set, return an error otherwise. Let **compressed bitmap** be the `<data>` part of the data url. +3. Generate an **uncompressed bitmap** by base64-decoding [[RFC4648](https://datatracker.ietf.org/doc/html/rfc4648)] the **compressed bitmap** and then decompressing the result using ZLIB [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)]. +4. Generate the **bitmap** by deserializing the **uncompressed bitmap** according to the [roaring bitmap serialization format](https://github.com/RoaringBitmap/RoaringFormatSpec/). +5. Return the **bitmap**. + +### Validation Algorithm + +The following steps MUST be followed when checking whether a verifiable credential is revoked: + +1. Let **credential** be a verifiable credential containing a `credentialStatus` whose `type` is `RevocationBitmap2022`. +2. Let **revocation bitmap URL** be the `id` field of the **credential**'s `credentialStatus`. +3. Resolve the **revocation bitmap URL** to a **revocation bitmap service** in the issuer's DID document, and verify that the service `type` is `RevocationBitmap2022`. Return an error otherwise. +4. Expand the endpoint of the **revocation bitmap service** into a **revocation bitmap** according to the [service endpoint expansion algorithm](#service-endpoint-expansion-algorithm). +5. Let **revocation index** be the integer value of the `revocationBitmapIndex` property contained in the `credentialStatus` of the **credential**. +6. Let **revoked** be the value of the bit at index **revocation index** in the **revocation bitmap**. +7. Return `true` if **revoked** is 1, `false` otherwise. + +## Test Vectors + +This section provides test vectors to validate implementations against. + +### Test Vector 1 + +The following data URL decodes to a bitmap of length 0 where no index is revoked: + +`"data:application/octet-stream;base64,ZUp5ek1tQUFBd0FES0FCcg=="` + +### Test Vector 2 + +The following data URL decodes to a bitmap of length 3 where indices `5`, `398`, and `67000` are revoked: + +`"data:application/octet-stream;base64,ZUp5ek1tQmdZR0lBQVVZZ1pHQ1FBR0laSUdabDZHUGN3UW9BRXVvQjlB"`. + +### Test Vector 3 + +The following data URL decodes to a bitmap of length 16384 where all indices are revoked: + +`"data:application/octet-stream;base64,ZUp6dHhERVJBQ0FNQkxESEFWS1lXZkN2Q3E0MmFESmtyMlNrM0ROckFLQ2RBQUFBQUFBQTMzbGhHZm9q"` + +## Rationale + +This section describes the rationale behind some of the design decisions of this specification. + +### Compression and maximum size + +Considering that messages published to the Tangle cannot exceed [32 KiB](https://github.com/iotaledger/tips/blob/main/tips/TIP-0006/tip-0006.md#message-validation) in size, and that larger messages have increased requirements, the use of compression was assessed. +The precise size of a serialized bitmap varies based on the number and distribution of revoked indices. When indices are revoked uniformly randomly, roughly 100,000 - 200,000 can be achieved in a DID Document with compression, and significantly more if consecutive indices are revoked. + +ZLIB [[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950)] was chosen for having a free and open source software licence and being one of the most widely used compression schemes, which enhances the accessibility of this specification. Some other assessed algorithms produced only marginally better compression ratios but had far fewer existing implementations across different programming languages. + +### Compressed Bitstring vs. Roaring Bitmap + +Because of its space efficiency, a roaring bitmap is preferred for representing a bitmap in-memory. To avoid the dependency on roaring bitmap, we considered using a compressed bitstring as the serialization format. However, serialization of such a bitstring was 2-3x slower compared to roaring's serialization format, which becomes an issue on resource-constrained devices (e.g. smartphones) or in web browsers. + +### Comparison to `RevocationList2020` and `StatusList2021` + +The [RevocationList2020 specification](https://w3c-ccg.github.io/vc-status-rl-2020/) and [StatusList2021 specification](https://w3c-ccg.github.io/vc-status-list-2021/) both describe a similar revocation mechanism using a verifiable credential that contains a bitmap, similar to the `RevocationBitmap2022` approach. The credential is hosted outside of the DID document and the verifier thus needs to fetch it from an external resource, likely one controlled by the issuer. This has privacy implications as the issuer can track where a fetch request for the credential came from and potentially infer who the credential was verified by and for what purpose. The issuer can also potentially infer which credential was checked. Because `RevocationBitmap2022` is embedded in the issuer's DID document, which can be obtained without the their knowledge, this approach does not suffer from these privacy shortcomings. See also the [privacy considerations](#privacy-considerations). + +A downside of embedding the revocation list in the DID document is that storage in a distributed ledger (DLT) is usually more expensive than other storage hosting solutions. The DLT might also impose message size limitations, capping the total number of revocations that can be done (see also [compression](#compression-and-maximum-size)). + +Another difference is that `RevocationList2020` specifies a minimum initial size of 131,072 for its bitstring, to mitigate the potential for correlating individuals when few credentials have been issued. `RevocationBitmap2022` uses a roaring bitmap instead of a bitstring, so the maximum size is not fixed (apart from the upper bound of an unsigned 32-bit integer). This means the bitmap cannot be used to correlate small populations without more information not present in the bitmap itself. However, both schemes still reveal publicly how many credentials have been revoked, which could be used to infer other information if more knowledge about how an issuer assigns credential revocation indexes is known. + +`StatusList2021` allows for explicitly stating the purpose of the list, currently either _revocation_ or _suspension_. This specification does not mandate that revoked credentials cannot be unrevoked, which means a `RevocationBitmap2022` can effectively also be used as a suspension list. + +### Privacy Considerations + +Because the revocation bitmap is embedded in the DID document, and thus available without contacting the issuer directly, the issuer cannot correlate how a holder uses their credential. + +An observer finding a service of type `RevocationBitmap2022` in a DID document can infer that this DID belongs to an issuer. However, DIDs of issuers tend to be publicly known, in contrast to DIDs of other entities, so this is unlikely to present an issue. External observers can monitor the frequency of revocations and potentially the total number of issued credentials, depending on how the issuer assigns credential indices (e.g. starting from 0 and incrementing the index for each issued credential). \ No newline at end of file diff --git a/docs/content/references/iota-identity/revocation-timeframe-2024.mdx b/docs/content/references/iota-identity/revocation-timeframe-2024.mdx new file mode 100644 index 00000000000..c4f7fb84ed7 --- /dev/null +++ b/docs/content/references/iota-identity/revocation-timeframe-2024.mdx @@ -0,0 +1,148 @@ +--- +title: Revocation Timeframe 2024 +sidebar_label: Revocation Timeframe 2024 +description: The specification for an embeddable time-based revocation method - `RevocationTimeframe2024`. +image: /img/identity/icon.png +tags: + - reference + - identity + - did +--- + +# Revocation Timeframe 2024 + +## Abstract + +This specification describes a new revocation mechanism - `RevocationTimeframe2024` - that extends [`RevocationBitmap2022`](./revocation-bitmap-2022.mdx) +to address its linkability security concerns, at least when used in combination with selectively disclosable (SD-able) VCs. + +## Introduction + +`RevocationBitmap2022` allows for a simple and straightforward way for an issuer to invalidate an issued VC before its expiration. While this method prevents the analysis of usage by the issuer through hosting the revocation information on-chain +it comes with a high risk of linkability by colluding verifiers as they can store the bitmap index and use it to identify users. Additionally, verifiers can monitor the revocation information and at a later point, outside of any interaction, check the revocation status. +To address this privacy concern, `RevocationTimeframe2024` was designed as an extension that builds on top of `RevocationBitmap2022`, therefore sharing +most of its logic. + +## Concepts +`RevocationTimeframe2024` should be used together with selectively disclosable credentials - either [SD-JWT](../../iota-identity/how-tos/verifiable-credentials/selective-disclosure) +or [ZK-SD](../../iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure) - in order to conceal to the verifier +the credential's index in the issuer's revocation bitmap to avoid linkability. + +### Validity Timeframe + +If the revocation index is concealed from the verifier how can it assert the validity of the presented credential? +To solve this issue `RevocationTimeframe2024` introduces the concept of a _validity timeframe_, i.e. a limited time span +in which the credential is guaranteed to be non-revoked. By having a validity timeframe embedded in the credential's status +the verifier can prove the credential's validity by verifying the credential's signature. + +The downside of this mechanism is that the credential holder using `RevocationTimeframe2024` has to contact the credential's issuer +to update the signature - i.e. re-issue the credential - at the end of the credentials validity timeframe. Furthermore, +given how a credentials validity timeframe proves the validity of the credential itself, the updates made by the issuer to the credential's status - +i.e., revoking or un-revoking it - won't be reflected on the credential until a new validity timeframe starts. For this reason, +issuers should choose a validity timeframe length with respect to how frequently they expect to change the credential's status; +frequent updates deem shorter validity timeframes. + +:::note +A credential holder does not need to have its credential re-issued at the end of every validity timeframe, but only when +they need to present the credential to a verifier and its validity timeframe has expired. +::: + +## Data Model + +### Revocation Timeframe Status + +For an issuer to enable verifiers to check the status of a verifiable credential, the [`credentialStatus`](https://www.w3.org/TR/vc-data-model/#status) property MUST be specified with the following properties: + +| Property | Description | +| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `id` | The constraints on the `id` property are listed in the [Verifiable Credentials Data Model specification](https://www.w3.org/TR/vc-data-model/). The `id` MUST be a [DID URL](https://www.w3.org/TR/did-core/#did-url-syntax) that is the URL to a [Revocation Timeframe Service](#revocation-timeframe-service) in the DID Document of the issuer. | +| `type` | The `type` property MUST be `"RevocationTimeframe2024"`. | +| `revocationBitmapIndex` | The `revocationBitmapIndex` property MUST be an unsigned, 32-bit integer expressed as a string. This is the index of the credential in the issuers revocation bitmap. Each index SHOULD be unique among all credentials linking to the same [Revocation Timeframe Service](#revocation-timeframe-service). To ensure user unlinkability, this value MUST NOT be disclosed to the verifier (this is done by default). | +| `startValidityTimeframe`| The `startValidityTimeframe` property MUST be a [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339)-compliant timestamp. | +| `endValidityTimeframe`| The `endValidityTimeframe` property MUST be a [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339)-compliant timestamp. | + +#### Example + +An example of a verifiable credential with a `credentialStatus` of type `RevocationTimeframe2024`. + +```json +{ + "@context": "https://www.w3.org/2018/credentials/v1", + "id": "https://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "credentialSubject": { + "id": "did:iota:B8DucnzULJ9E8cmaReYoePU2b7UKE9WKxyEVov8tQA7H", + "GPA": "4.0", + "degree": "Bachelor of Science and Arts", + "name": "Alice" + }, + "issuer": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "issuanceDate": "2022-06-13T08:04:36Z", + "credentialStatus": { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#my-revocation-service", + "type": "RevocationTimeframe2024", + "revocationBitmapIndex": "5" + "startValidityTimeframe": "2024-05-03T08:00:00Z", + "endValidityTimeframe": "2024-05-03T08:05:00Z", + }, +} +``` + +### Revocation Timeframe Service + +To allow verifiers to check the status of a [Revocation Timeframe Status](#revocation-timeframe-status), the DID document of the credential issuer MUST contain a [service](https://www.w3.org/TR/did-core/#services) with the following properties: + +| Property | Description | +| :---------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | The constraints on the `id` property are listed in the [DID Core service specification](https://www.w3.org/TR/did-core/#services). The `id` property MUST be a DID URL uniquely identifying the revocation bitmap. | +| `type` | The `type` property MUST be `"RevocationTimeframe2024"`. | +| `serviceEndpoint` | The `serviceEndpoint` MUST be generated according to the service endpoint generation algorithm described in [`RevocationBitmap2022`](./revocation-bitmap-2022.mdx#service-endpoint-generation-algorithm). | + +#### Example + +An example of an issuers DID document where credential `"5"` in the `#revocation` service is revoked: + +```json +{ + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "verificationMethod": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#key-1", + "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z3hgM9fNkhwgT5mECbj1HdKoFNZgpffwQYEV8WBVHphXq" + } + ], + "capabilityInvocation": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#sign-0", + "controller": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw", + "type": "Ed25519VerificationKey2018", + "publicKeyMultibase": "z83F6zbD3KqaxvQhqo25LvSXzoDdpZmp3EpPVonSVACwZ" + } + ], + "service": [ + { + "id": "did:iota:EvaQhPXXsJsGgxSXGhZGMCvTt63KuAFtaGThx6a5nSpw#revocation", + "type": "RevocationTimeframe2024", + "serviceEndpoint": "data:application/octet-stream;base64,ZUp5ek1tQmdZR1NBQUFFZ1ptVUFBQWZPQUlF" + } + ] +} +``` + +## Algorithms + +For generation and expansion of the service endpoint see [`RevocationBitmap2022`](./revocation-bitmap-2022.mdx#algorithms). + +### Validation Algorithm + +The following steps MUST be followed when checking whether a verifiable credential is revoked: + +1. Let **credential** be a verifiable credential containing a `credentialStatus` whose `type` is `RevocationTimeframe2024`. +2. Let **now** be the string serialization of the RFC3339 timestamp representing the current UTC time. +3. Let **start validity timeframe** and **end validity timeframe** be the RFC3339 timestamps of the `startValidityTimeframe` and `endValidityTimeframe` (respectively) properties contained in the `credentialStatus` of the **credential**. +4. Return `true` if `startValidityTimeframe <= now < endValidityTimeframe`, `false` otherwise. + +## Test Vectors +See [`RevocationBitmap2022`](./revocation-bitmap-2022.mdx#test-vectors). \ No newline at end of file diff --git a/docs/content/sidebars/developer.js b/docs/content/sidebars/developer.js index 32192dd666b..e8ac1eb1bb5 100644 --- a/docs/content/sidebars/developer.js +++ b/docs/content/sidebars/developer.js @@ -212,7 +212,7 @@ const developer = [ { type: 'category', label: 'NFT', - items: ['developer/iota-101/nft/create-nft', 'developer/iota-101/nft/rent-nft'], + items: ['developer/iota-101/nft/create-nft', 'developer/iota-101/nft/rent-nft', 'developer/iota-101/nft/marketplace'], }, { type: 'category', diff --git a/docs/content/sidebars/identity.js b/docs/content/sidebars/identity.js new file mode 100644 index 00000000000..ceba6878c5e --- /dev/null +++ b/docs/content/sidebars/identity.js @@ -0,0 +1,90 @@ +// Copyright (c) Mysten Labs, Inc. +// Modifications Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 +const identity = [ + 'iota-identity/index', + { + type: 'category', + label: 'Getting Started', + collapsed: false, + items: ['iota-identity/getting-started/rust'], + }, + { + type: 'category', + label: 'Explanations', + items: [ + 'iota-identity/explanations/decentralized-identifiers', + 'iota-identity/explanations/verifiable-credentials', + 'iota-identity/explanations/verifiable-presentations', + 'iota-identity/explanations/about-identity-objects', + ], + }, + { + type: 'category', + label: 'How To', + items: [ + { + type: 'category', + label: 'Decentralized Identifiers (DID)', + items: [ + 'iota-identity/how-tos/decentralized-identifiers/create', + 'iota-identity/how-tos/decentralized-identifiers/update', + 'iota-identity/how-tos/decentralized-identifiers/resolve', + 'iota-identity/how-tos/decentralized-identifiers/delete', + ], + }, + { + type: 'category', + label: 'Verifiable Credentials', + items: [ + 'iota-identity/how-tos/verifiable-credentials/create', + 'iota-identity/how-tos/verifiable-credentials/revocation', + 'iota-identity/how-tos/verifiable-credentials/selective-disclosure', + 'iota-identity/how-tos/verifiable-credentials/zero-knowledge-selective-disclosure', + ], + }, + { + type: 'category', + label: 'Verifiable Presentations', + items: ['iota-identity/how-tos/verifiable-presentations/create-and-validate'], + }, + { + type: 'category', + label: 'Domain Linkage', + items: ['iota-identity/how-tos/domain-linkage/create-and-verify'], + }, + 'iota-identity/how-tos/key-storage', + ], + }, + { + type: 'category', + label: 'References', + collapsed: true, + items: [ + { + type: 'category', + label: 'API', + items: [ + { + type: 'link', + label: 'Rust', + href: 'https://docs.rs/identity_iota/latest/identity_iota/index.html', + }, + ], + }, + { + type: 'category', + label: 'Specifications', + items: [ + 'references/iota-identity/overview', + 'references/iota-identity/iota-did-method-spec', + 'references/iota-identity/revocation-bitmap-2022', + 'references/iota-identity/revocation-timeframe-2024', + ], + }, + ], + }, + 'iota-identity/contribute', +]; + +module.exports = identity; diff --git a/docs/content/sidebars/ts-sdk.js b/docs/content/sidebars/ts-sdk.js index 3ce2111226e..6951b07e0f4 100644 --- a/docs/content/sidebars/ts-sdk.js +++ b/docs/content/sidebars/ts-sdk.js @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 import typedocSidebar from '../references/ts-sdk/api/typedoc-sidebar.cjs'; -const references = [ +const tsSDK = [ { type: 'category', label: 'Typescript SDK', @@ -133,4 +133,4 @@ const references = [ }, ]; -module.exports = references; +module.exports = tsSDK; diff --git a/docs/examples/move/nft_marketplace/README.md b/docs/examples/move/nft_marketplace/README.md index 42cb414ab65..80b330362ff 100644 --- a/docs/examples/move/nft_marketplace/README.md +++ b/docs/examples/move/nft_marketplace/README.md @@ -1,4 +1,4 @@ -# Marketplace Guide +# Marketplace Extension Usage ## Modules diff --git a/docs/site/configs/preContent.tsx b/docs/site/configs/preContent.tsx new file mode 100644 index 00000000000..a7f9798b920 --- /dev/null +++ b/docs/site/configs/preContent.tsx @@ -0,0 +1,9 @@ +// Copyright (c) 2024 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import Admonition from '@theme/Admonition'; + +export default { + '/iota-identity*': <Admonition type='info'>IOTA Identity for Rebased is currently in alpha and may still be subject to significant changes</Admonition>, +}; diff --git a/docs/site/docusaurus.config.js b/docs/site/docusaurus.config.js index 3edac2ca385..bdb717867cb 100644 --- a/docs/site/docusaurus.config.js +++ b/docs/site/docusaurus.config.js @@ -186,7 +186,8 @@ const config = { type: "text/css", }, ], - themes: ["@docusaurus/theme-live-codeblock", "@docusaurus/theme-mermaid", 'docusaurus-theme-search-typesense'], + themes: ["@docusaurus/theme-live-codeblock", "@docusaurus/theme-mermaid", 'docusaurus-theme-search-typesense', + '@saucelabs/theme-github-codeblock'], themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ ({ @@ -241,10 +242,6 @@ const config = { label: "Developers", to: "developer", }, - { - label: "TS SDK", - to: "references/ts-sdk/typescript/", - }, { label: "Node Operators", to: "operator", @@ -253,6 +250,14 @@ const config = { label: "References", to: "references", }, + { + label: "TS SDK", + to: "references/ts-sdk/typescript/", + }, + { + label: "IOTA Identity", + to: "iota-identity", + }, ], }, footer: { diff --git a/docs/site/package.json b/docs/site/package.json index 28d0b5926e7..5f0f7503310 100644 --- a/docs/site/package.json +++ b/docs/site/package.json @@ -36,6 +36,7 @@ "@iota/iota-sdk": "workspace:*", "@mdx-js/react": "^3.0.1", "@mui/material": "^5.15.19", + "@saucelabs/theme-github-codeblock": "^0.3.0", "@tanstack/react-query": "^5.50.1", "autoprefixer": "^10.4.19", "axios": "^1.7.4", diff --git a/docs/site/sidebars.js b/docs/site/sidebars.js index 0207505a354..88001f36f83 100644 --- a/docs/site/sidebars.js +++ b/docs/site/sidebars.js @@ -8,6 +8,7 @@ const aboutIota = require("../content/sidebars/about-iota.js"); const operator = require("../content/sidebars/operator.js"); const references = require("../content/sidebars/references.js"); const tsSDK = require("../content/sidebars/ts-sdk.js") +const identity = require("../content/sidebars/identity.js") const sidebars = { //whyIOTASidebar: why_iota, @@ -16,6 +17,7 @@ const sidebars = { aboutIotaSidebar: aboutIota, referencesSidebar: references, tsSDK: tsSDK, + identity: identity, }; module.exports = sidebars; diff --git a/docs/site/src/theme/DocItem/index.tsx b/docs/site/src/theme/DocItem/index.tsx new file mode 100644 index 00000000000..03b5768679f --- /dev/null +++ b/docs/site/src/theme/DocItem/index.tsx @@ -0,0 +1,26 @@ +/** + * SWIZZLED VERSION: 3.5.2 + * REASONS: + * - Add option to allow custom components before the DocItem + */ +import React from 'react'; +import DocItem from '@theme-original/DocItem'; +import type DocItemType from '@theme/DocItem'; +import type {WrapperProps} from '@docusaurus/types'; +import { useLocation } from '@docusaurus/router'; + +import preMDXComponents from '../../../configs/preContent'; + +type Props = WrapperProps<typeof DocItemType>; + +export default function DocItemWrapper(props: Props): JSX.Element { + const { pathname } = useLocation(); + const matchingKey = Object.keys(preMDXComponents).find((key) => new RegExp(key).test(pathname)); + + return ( + <> + {preMDXComponents[matchingKey]} + <DocItem {...props} /> + </> + ); +} diff --git a/docs/site/src/theme/MDXContent/index.tsx b/docs/site/src/theme/MDXContent/index.tsx index f2d505c0bb0..a94a55e771d 100644 --- a/docs/site/src/theme/MDXContent/index.tsx +++ b/docs/site/src/theme/MDXContent/index.tsx @@ -2,6 +2,7 @@ * SWIZZLED VERSION: 3.5.2 * REASONS: * - Add default components + * - Add FeedbackForm component */ import React from 'react'; import { MDXProvider } from '@mdx-js/react'; diff --git a/iota-execution/latest/iota-adapter/src/programmable_transactions/context.rs b/iota-execution/latest/iota-adapter/src/programmable_transactions/context.rs index d8d20638fc6..cfe7ff20ee5 100644 --- a/iota-execution/latest/iota-adapter/src/programmable_transactions/context.rs +++ b/iota-execution/latest/iota-adapter/src/programmable_transactions/context.rs @@ -1019,7 +1019,7 @@ mod checked { } } - impl<'vm, 'state, 'a> TypeTagResolver for ExecutionContext<'vm, 'state, 'a> { + impl TypeTagResolver for ExecutionContext<'_, '_, '_> { /// Retrieves the `TypeTag` corresponding to the provided `Type` by /// querying the Move VM runtime. fn get_type_tag(&self, type_: &Type) -> Result<TypeTag, ExecutionError> { @@ -1464,7 +1464,7 @@ mod checked { // TODO: `DataStore` will be reworked and this is likely to disappear. // Leaving this comment around until then as testament to better days to // come... - impl<'state, 'a> DataStore for IotaDataStore<'state, 'a> { + impl DataStore for IotaDataStore<'_, '_> { fn link_context(&self) -> AccountAddress { self.linkage_view.link_context() } diff --git a/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs b/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs index cc70099dc59..b382a447ffc 100644 --- a/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs +++ b/iota-execution/latest/iota-adapter/src/programmable_transactions/execution.rs @@ -1659,7 +1659,7 @@ mod checked { struct VectorElementVisitor<'a>(&'a PrimitiveArgumentLayout); - impl<'d, 'a> serde::de::Visitor<'d> for VectorElementVisitor<'a> { + impl<'d> serde::de::Visitor<'d> for VectorElementVisitor<'_> { type Value = (); fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1677,7 +1677,7 @@ mod checked { struct OptionElementVisitor<'a>(&'a PrimitiveArgumentLayout); - impl<'d, 'a> serde::de::Visitor<'d> for OptionElementVisitor<'a> { + impl<'d> serde::de::Visitor<'d> for OptionElementVisitor<'_> { type Value = (); fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/iota-execution/latest/iota-adapter/src/programmable_transactions/linkage_view.rs b/iota-execution/latest/iota-adapter/src/programmable_transactions/linkage_view.rs index 40a0270c3fe..b3bdf7948a8 100644 --- a/iota-execution/latest/iota-adapter/src/programmable_transactions/linkage_view.rs +++ b/iota-execution/latest/iota-adapter/src/programmable_transactions/linkage_view.rs @@ -303,7 +303,7 @@ impl From<&MovePackage> for LinkageInfo { } } -impl<'state> LinkageResolver for LinkageView<'state> { +impl LinkageResolver for LinkageView<'_> { type Error = IotaError; fn link_context(&self) -> AccountAddress { @@ -325,7 +325,7 @@ impl<'state> LinkageResolver for LinkageView<'state> { /// Remaining implementations delegated to state_view ************************ -impl<'state> ResourceResolver for LinkageView<'state> { +impl ResourceResolver for LinkageView<'_> { type Error = IotaError; fn get_resource( @@ -337,7 +337,7 @@ impl<'state> ResourceResolver for LinkageView<'state> { } } -impl<'state> ModuleResolver for LinkageView<'state> { +impl ModuleResolver for LinkageView<'_> { type Error = IotaError; fn get_module(&self, id: &ModuleId) -> Result<Option<Vec<u8>>, Self::Error> { @@ -345,7 +345,7 @@ impl<'state> ModuleResolver for LinkageView<'state> { } } -impl<'state> BackingPackageStore for LinkageView<'state> { +impl BackingPackageStore for LinkageView<'_> { fn get_package_object(&self, package_id: &ObjectID) -> IotaResult<Option<PackageObject>> { self.resolver.get_package_object(package_id) } diff --git a/iota-execution/latest/iota-adapter/src/temporary_store.rs b/iota-execution/latest/iota-adapter/src/temporary_store.rs index 10f5a7304f5..87587c322c9 100644 --- a/iota-execution/latest/iota-adapter/src/temporary_store.rs +++ b/iota-execution/latest/iota-adapter/src/temporary_store.rs @@ -531,7 +531,7 @@ impl<'backing> TemporaryStore<'backing> { } } -impl<'backing> TemporaryStore<'backing> { +impl TemporaryStore<'_> { // check that every object read is owned directly or indirectly by sender, // sponsor, or a shared object input pub fn check_ownership_invariants( @@ -652,7 +652,7 @@ impl<'backing> TemporaryStore<'backing> { } } -impl<'backing> TemporaryStore<'backing> { +impl TemporaryStore<'_> { /// Track storage gas for each mutable input object (including the gas coin) /// and each created object. Compute storage refunds for each deleted /// object. Will *not* charge anything, gas status keeps track of @@ -736,7 +736,7 @@ impl<'backing> TemporaryStore<'backing> { // Charge gas current - end //============================================================================== -impl<'backing> TemporaryStore<'backing> { +impl TemporaryStore<'_> { pub fn advance_epoch_safe_mode( &mut self, params: &AdvanceEpochParams, @@ -757,7 +757,7 @@ type ModifiedObjectInfo<'a> = ( Option<&'a Object>, ); -impl<'backing> TemporaryStore<'backing> { +impl TemporaryStore<'_> { fn get_input_iota( &self, id: &ObjectID, @@ -968,7 +968,7 @@ impl<'backing> TemporaryStore<'backing> { } } -impl<'backing> ChildObjectResolver for TemporaryStore<'backing> { +impl ChildObjectResolver for TemporaryStore<'_> { fn read_child_object( &self, parent: &ObjectID, @@ -1016,7 +1016,7 @@ impl<'backing> ChildObjectResolver for TemporaryStore<'backing> { } } -impl<'backing> Storage for TemporaryStore<'backing> { +impl Storage for TemporaryStore<'_> { fn reset(&mut self) { self.drop_writes(); } @@ -1068,7 +1068,7 @@ impl<'backing> Storage for TemporaryStore<'backing> { } } -impl<'backing> BackingPackageStore for TemporaryStore<'backing> { +impl BackingPackageStore for TemporaryStore<'_> { fn get_package_object(&self, package_id: &ObjectID) -> IotaResult<Option<PackageObject>> { // We first check the objects in the temporary store because in non-production // code path, it is possible to read packages that are just written in @@ -1102,7 +1102,7 @@ impl<'backing> BackingPackageStore for TemporaryStore<'backing> { } } -impl<'backing> ResourceResolver for TemporaryStore<'backing> { +impl ResourceResolver for TemporaryStore<'_> { type Error = IotaError; fn get_resource( diff --git a/iota-execution/latest/iota-adapter/src/type_layout_resolver.rs b/iota-execution/latest/iota-adapter/src/type_layout_resolver.rs index 4520d29b0ad..941c3cae02a 100644 --- a/iota-execution/latest/iota-adapter/src/type_layout_resolver.rs +++ b/iota-execution/latest/iota-adapter/src/type_layout_resolver.rs @@ -37,7 +37,7 @@ impl<'state, 'vm> TypeLayoutResolver<'state, 'vm> { } } -impl<'state, 'vm> LayoutResolver for TypeLayoutResolver<'state, 'vm> { +impl LayoutResolver for TypeLayoutResolver<'_, '_> { fn get_annotated_layout( &mut self, struct_tag: &StructTag, @@ -58,13 +58,13 @@ impl<'state, 'vm> LayoutResolver for TypeLayoutResolver<'state, 'vm> { } } -impl<'state> BackingPackageStore for NullIotaResolver<'state> { +impl BackingPackageStore for NullIotaResolver<'_> { fn get_package_object(&self, package_id: &ObjectID) -> IotaResult<Option<PackageObject>> { self.0.get_package_object(package_id) } } -impl<'state> ResourceResolver for NullIotaResolver<'state> { +impl ResourceResolver for NullIotaResolver<'_> { type Error = IotaError; fn get_resource( diff --git a/iota-execution/latest/iota-move-natives/src/object_runtime/mod.rs b/iota-execution/latest/iota-move-natives/src/object_runtime/mod.rs index 9244a158540..099f3c8537d 100644 --- a/iota-execution/latest/iota-move-natives/src/object_runtime/mod.rs +++ b/iota-execution/latest/iota-move-natives/src/object_runtime/mod.rs @@ -696,7 +696,7 @@ pub fn get_all_uids( struct UIDTraversalV1<'i>(&'i mut BTreeSet<ObjectID>); struct UIDCollectorV1<'i>(&'i mut BTreeSet<ObjectID>); - impl<'i> AV::Traversal for UIDTraversalV1<'i> { + impl AV::Traversal for UIDTraversalV1<'_> { type Error = AV::Error; fn traverse_struct(&mut self, driver: &mut AV::StructDriver) -> Result<(), Self::Error> { @@ -709,7 +709,7 @@ pub fn get_all_uids( } } - impl<'i> AV::Traversal for UIDCollectorV1<'i> { + impl AV::Traversal for UIDCollectorV1<'_> { type Error = AV::Error; fn traverse_address(&mut self, value: AccountAddress) -> Result<(), Self::Error> { self.0.insert(value.into()); diff --git a/iota-execution/latest/iota-move-natives/src/object_runtime/object_store.rs b/iota-execution/latest/iota-move-natives/src/object_runtime/object_store.rs index 6883e4b6b46..50ef7d78cc3 100644 --- a/iota-execution/latest/iota-move-natives/src/object_runtime/object_store.rs +++ b/iota-execution/latest/iota-move-natives/src/object_runtime/object_store.rs @@ -164,7 +164,7 @@ macro_rules! fetch_child_object_unbounded { }}; } -impl<'a> Inner<'a> { +impl Inner<'_> { fn receive_object_from_store( &self, owner: ObjectID, diff --git a/iota-execution/latest/iota-verifier/src/id_leak_verifier.rs b/iota-execution/latest/iota-verifier/src/id_leak_verifier.rs index ab6c62783e7..6df340594d7 100644 --- a/iota-execution/latest/iota-verifier/src/id_leak_verifier.rs +++ b/iota-execution/latest/iota-verifier/src/id_leak_verifier.rs @@ -273,7 +273,7 @@ impl<'a> IDLeakAnalysis<'a> { } } -impl<'a> TransferFunctions for IDLeakAnalysis<'a> { +impl TransferFunctions for IDLeakAnalysis<'_> { type Error = ExecutionError; type State = AbstractState; @@ -301,7 +301,7 @@ impl<'a> TransferFunctions for IDLeakAnalysis<'a> { } } -impl<'a> AbstractInterpreter for IDLeakAnalysis<'a> {} +impl AbstractInterpreter for IDLeakAnalysis<'_> {} fn call( verifier: &mut IDLeakAnalysis, diff --git a/iota-execution/src/latest.rs b/iota-execution/src/latest.rs index 4f1c5ebe57d..cc556480a73 100644 --- a/iota-execution/src/latest.rs +++ b/iota-execution/src/latest.rs @@ -188,7 +188,7 @@ impl executor::Executor for Executor { } } -impl<'m> verifier::Verifier for Verifier<'m> { +impl verifier::Verifier for Verifier<'_> { fn meter(&self, config: MeterConfig) -> Box<dyn Meter> { Box::new(IotaVerifierMeter::new(config)) } diff --git a/nre/validator_tasks.md b/nre/validator_tasks.md index 00f4e875b45..06d741d4131 100644 --- a/nre/validator_tasks.md +++ b/nre/validator_tasks.md @@ -91,7 +91,7 @@ Iota Node uses the following ports by default: | protocol/port | reachability | purpose | | ------------- | ---------------- | --------------------------------- | | TCP/8080 | inbound | protocol/transaction interface | -| UDP/8081 | inbound/outbound | primary interface | +| TCP/8081 | inbound/outbound | primary interface | | UDP/8084 | inbound/outbound | peer to peer state sync interface | | TCP/8443 | outbound | metrics pushing | | TCP/9184 | localhost | metrics scraping | diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f8d5ad6e06..5233dee99b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -304,7 +304,7 @@ importers: version: 5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0) vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@22.7.3)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@22.7.3)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) apps/explorer: dependencies: @@ -505,8 +505,8 @@ importers: specifier: ^10.4.19 version: 10.4.20(postcss@8.4.47) happy-dom: - specifier: ^10.5.1 - version: 10.11.2 + specifier: ^15.11.7 + version: 15.11.7 onchange: specifier: ^7.1.0 version: 7.1.0 @@ -533,7 +533,7 @@ importers: version: 3.3.0(rollup@4.22.4)(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0)) vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) apps/ui-icons: devDependencies: @@ -936,8 +936,8 @@ importers: specifier: ^3.0.2 version: 3.0.2 happy-dom: - specifier: ^10.5.1 - version: 10.11.2 + specifier: ^15.11.7 + version: 15.11.7 html-webpack-plugin: specifier: ^5.5.3 version: 5.6.0(webpack@5.95.0(@swc/core@1.7.28)(webpack-cli@5.1.4)) @@ -988,7 +988,7 @@ importers: version: 4.3.2(typescript@5.6.2)(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0)) vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) web-ext: specifier: ^7.6.2 version: 7.12.0(body-parser@1.20.3) @@ -1359,6 +1359,9 @@ importers: '@mui/material': specifier: ^5.15.19 version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.9)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.9)(react@18.3.1))(@types/react@18.3.9)(react@18.3.1))(@types/react@18.3.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@saucelabs/theme-github-codeblock': + specifier: ^0.3.0 + version: 0.3.0 '@tanstack/react-query': specifier: ^5.50.1 version: 5.56.2(react@18.3.1) @@ -1492,7 +1495,7 @@ importers: version: 5.6.2 vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@22.7.3)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@22.7.3)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) sdk/build-scripts: dependencies: @@ -1755,7 +1758,7 @@ importers: version: 5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0) vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@22.7.3)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@22.7.3)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) sdk/graphql-transport: dependencies: @@ -1810,7 +1813,7 @@ importers: version: 5.6.2 vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -1844,7 +1847,7 @@ importers: version: 5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0) vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@22.7.3)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@22.7.3)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -1884,7 +1887,7 @@ importers: version: 5.6.2 vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) sdk/move-bytecode-template: devDependencies: @@ -1899,7 +1902,7 @@ importers: version: 5.6.2 vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@22.7.3)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@22.7.3)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) wasm-pack: specifier: ^0.13.0 version: 0.13.0 @@ -2002,7 +2005,7 @@ importers: version: 5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0) vitest: specifier: ^2.0.1 - version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + version: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) wait-on: specifier: ^7.2.0 version: 7.2.0 @@ -6253,6 +6256,9 @@ packages: '@rushstack/ts-command-line@4.19.1': resolution: {integrity: sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==} + '@saucelabs/theme-github-codeblock@0.3.0': + resolution: {integrity: sha512-+8xWxBfN+I8StJ0QXERMbGf+BHwRXHWV3mFl9uDayXERiZ/rR93d0nAS3s9s/rKjqh/YSm/4dThEkBNBLnGs4Q==} + '@scure/base@1.1.9': resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} @@ -10817,8 +10823,9 @@ packages: engines: {node: '>=0.4.7'} hasBin: true - happy-dom@10.11.2: - resolution: {integrity: sha512-rzgmLjLkhyaOdFEyU8CWXzbgyCyM7wJHLqhaoeEVSTyur1fjcUaiNTHx+D4CPaLvx16tGy+SBPd9TVnP/kzL3w==} + happy-dom@15.11.7: + resolution: {integrity: sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==} + engines: {node: '>=18.0.0'} har-schema@2.0.0: resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} @@ -16502,10 +16509,6 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} - whatwg-encoding@2.0.0: - resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} - engines: {node: '>=12'} - whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -22732,6 +22735,8 @@ snapshots: transitivePeerDependencies: - '@types/node' + '@saucelabs/theme-github-codeblock@0.3.0': {} + '@scure/base@1.1.9': {} '@scure/bip32@1.4.0': @@ -24967,7 +24972,7 @@ snapshots: pathe: 1.1.2 picocolors: 1.1.0 sirv: 2.0.4 - vitest: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) + vitest: 2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0) '@vitest/utils@0.33.0': dependencies: @@ -28835,13 +28840,10 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 - happy-dom@10.11.2: + happy-dom@15.11.7: dependencies: - css.escape: 1.5.1 entities: 4.5.0 - iconv-lite: 0.6.3 webidl-conversions: 7.0.0 - whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 har-schema@2.0.0: {} @@ -35457,7 +35459,7 @@ snapshots: sass: 1.79.3 terser: 5.34.0 - vitest@2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0): + vitest@2.1.1(@types/node@20.16.9)(@vitest/ui@0.33.0)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0): dependencies: '@vitest/expect': 2.1.1 '@vitest/mocker': 2.1.1(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@20.16.9)(sass@1.79.3)(terser@5.34.0)) @@ -35481,7 +35483,7 @@ snapshots: optionalDependencies: '@types/node': 20.16.9 '@vitest/ui': 0.33.0(vitest@2.1.1) - happy-dom: 10.11.2 + happy-dom: 15.11.7 jsdom: 24.1.3 transitivePeerDependencies: - less @@ -35494,7 +35496,7 @@ snapshots: - supports-color - terser - vitest@2.1.1(@types/node@22.7.3)(happy-dom@10.11.2)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0): + vitest@2.1.1(@types/node@22.7.3)(happy-dom@15.11.7)(jsdom@24.1.3)(msw@2.4.9(typescript@5.6.2))(sass@1.79.3)(terser@5.34.0): dependencies: '@vitest/expect': 2.1.1 '@vitest/mocker': 2.1.1(msw@2.4.9(typescript@5.6.2))(vite@5.4.8(@types/node@22.7.3)(sass@1.79.3)(terser@5.34.0)) @@ -35517,7 +35519,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.7.3 - happy-dom: 10.11.2 + happy-dom: 15.11.7 jsdom: 24.1.3 transitivePeerDependencies: - less @@ -36125,10 +36127,6 @@ snapshots: websocket-extensions@0.1.4: {} - whatwg-encoding@2.0.0: - dependencies: - iconv-lite: 0.6.3 - whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3