Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Document safe use of Twox64Concat #601

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
4 changes: 4 additions & 0 deletions dip-template/pallets/pallet-postit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,14 @@ pub mod pallet {
type Username: Encode + Decode + TypeInfo + MaxEncodedLen + Clone + PartialEq + Debug + Default;
}

// Safety: The hash is calculated on chain. The hashing algorithm provided by
// frame_system::Config must be cryptographically secure.
#[pallet::storage]
#[pallet::getter(fn posts)]
pub type Posts<T> = StorageMap<_, Twox64Concat, <T as frame_system::Config>::Hash, PostOf<T>>;

// Safety: The hash is calculated on chain. The hashing algorithm provided by
// frame_system::Config must be cryptographically secure.
#[pallet::storage]
#[pallet::getter(fn comments)]
pub type Comments<T> = StorageMap<_, Twox64Concat, <T as frame_system::Config>::Hash, CommentOf<T>>;
Expand Down
8 changes: 3 additions & 5 deletions nodes/parachain/src/chain_spec/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ use cumulus_primitives_core::ParaId;
use hex_literal::hex;
use runtime_common::constants::KILT;
use sc_chain_spec::ChainType;
use sp_core::crypto::UncheckedInto;
use sp_core::sr25519;
use sp_core::{crypto::UncheckedInto, sr25519};

use clone_runtime::{
BalancesConfig, ParachainInfoConfig, PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SudoConfig,
SystemConfig,
BalancesConfig, CollatorSelectionConfig, ParachainInfoConfig, PolkadotXcmConfig, RuntimeGenesisConfig,
SessionConfig, SudoConfig, SystemConfig, WASM_BINARY,
};
use clone_runtime::{CollatorSelectionConfig, WASM_BINARY};
use runtime_common::{AccountId, AuthorityId, Balance};

use super::{get_account_id_from_seed, get_from_seed, get_properties, DEFAULT_PARA_ID};
Expand Down
16 changes: 12 additions & 4 deletions pallets/attestation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub mod pallet {
};

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);

/// Type of a claim hash.
pub type ClaimHashOf<T> = <T as frame_system::Config>::Hash;
Expand Down Expand Up @@ -194,9 +194,9 @@ pub mod pallet {
///
/// It maps from a delegation ID to a vector of claim hashes.
#[pallet::storage]
#[pallet::getter(fn external_attestations)]
pub type ExternalAttestations<T> =
StorageDoubleMap<_, Twox64Concat, AuthorizationIdOf<T>, Blake2_128Concat, ClaimHashOf<T>, bool, ValueQuery>;
// #[pallet::getter(fn external_attestations)]
pub(crate) type ExternalAttestations<T> =
StorageDoubleMap<_, Blake2_128Concat, AuthorizationIdOf<T>, Blake2_128Concat, ClaimHashOf<T>, bool, ValueQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
Expand Down Expand Up @@ -501,6 +501,13 @@ pub mod pallet {
}

impl<T: Config> Pallet<T> {
// TODO: Delete this and add `#[pallet::getter(fn external_attestations)]` once
// the migration is over.
pub fn external_attestations(authorization_id: AuthorizationIdOf<T>, claim_hash: ClaimHashOf<T>) -> bool {
ExternalAttestations::<T>::get(&authorization_id, claim_hash)
|| migrations::v1::ExternalAttestations::<T>::get(&authorization_id, claim_hash)
}

fn remove_attestation(
authorized_by: AuthorizedByOf<T>,
attestation: AttestationDetailsOf<T>,
Expand All @@ -520,6 +527,7 @@ pub mod pallet {
Attestations::<T>::remove(claim_hash);
if let Some(authorization_id) = &attestation.authorization_id {
ExternalAttestations::<T>::remove(authorization_id, claim_hash);
migrations::v1::ExternalAttestations::<T>::remove(authorization_id, claim_hash);
}
if !attestation.revoked {
Self::deposit_event(Event::AttestationRevoked {
Expand Down
17 changes: 17 additions & 0 deletions pallets/attestation/src/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ where
)
}

pub mod v1 {
use frame_support::{pallet_prelude::*, storage_alias, Blake2_128Concat};

use crate::{AuthorizationIdOf, ClaimHashOf, Config, Pallet};

#[storage_alias]
pub(crate) type ExternalAttestations<T: Config> = StorageDoubleMap<
Pallet<T>,
Twox64Concat,
AuthorizationIdOf<T>,
Blake2_128Concat,
ClaimHashOf<T>,
bool,
ValueQuery,
>;
}

#[cfg(test)]
pub mod test {
use ctype::mock::get_ctype_hash;
Expand Down
27 changes: 27 additions & 0 deletions pallets/attestation/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,19 @@ pub fn insert_attestation<T: Config>(claim_hash: ClaimHashOf<T>, details: Attest
}
}

pub fn insert_attestation_v1<T: Config>(claim_hash: ClaimHashOf<T>, details: AttestationDetailsOf<T>) {
crate::AttestationStorageDepositCollector::<T>::create_deposit(
details.deposit.owner.clone(),
details.deposit.amount,
)
.expect("Should have balance");

crate::Attestations::<T>::insert(claim_hash, details.clone());
if let Some(delegation_id) = details.authorization_id.as_ref() {
crate::migrations::v1::ExternalAttestations::<T>::insert(delegation_id, claim_hash, true)
}
}

pub fn sr25519_did_from_public_key(public_key: &[u8; 32]) -> SubjectId {
MultiSigner::from(sr25519::Public(*public_key)).into_account().into()
}
Expand Down Expand Up @@ -352,6 +365,7 @@ pub(crate) mod runtime {
/// endowed accounts with balances
balances: Vec<(AccountIdOf<Test>, BalanceOf<Test>)>,
attestations: Vec<(ClaimHashOf<Test>, AttestationDetailsOf<Test>)>,
attestations_v1: Vec<(ClaimHashOf<Test>, AttestationDetailsOf<Test>)>,
}

impl ExtBuilder {
Expand All @@ -373,6 +387,15 @@ pub(crate) mod runtime {
self
}

#[must_use]
pub fn with_attestations_v1(
mut self,
attestations: Vec<(ClaimHashOf<Test>, AttestationDetailsOf<Test>)>,
) -> Self {
self.attestations_v1 = attestations;
self
}

pub fn build(self) -> sp_io::TestExternalities {
let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
pallet_balances::GenesisConfig::<Test> {
Expand Down Expand Up @@ -401,6 +424,10 @@ pub(crate) mod runtime {
for (claim_hash, details) in self.attestations {
insert_attestation::<Test>(claim_hash, details);
}

for (claim_hash, details) in self.attestations_v1 {
insert_attestation_v1::<Test>(claim_hash, details);
}
});

ext
Expand Down
57 changes: 56 additions & 1 deletion pallets/attestation/src/tests/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use frame_support::{assert_noop, assert_ok, traits::fungible::InspectHold};
use kilt_support::mock::mock_origin::DoubleOrigin;
use sp_runtime::traits::Zero;

use crate::{self as attestation, mock::*, AttesterOf, Config, Event, HoldReason};
use crate::{self as attestation, migrations, mock::*, AttesterOf, Config, Event, ExternalAttestations, HoldReason};

#[test]
fn test_remove() {
Expand Down Expand Up @@ -106,6 +106,9 @@ fn test_remove_authorized() {
.with_ctypes(vec![(attestation.ctype_hash, revoker.clone())])
.with_attestations(vec![(claim_hash, attestation)])
.build_and_execute_with_sanity_tests(|| {
assert!(Attestation::attestations(claim_hash).is_some());
assert!(Attestation::external_attestations(revoker.clone(), claim_hash));

assert_ok!(Attestation::remove(
DoubleOrigin(ACCOUNT_00, revoker.clone()).into(),
claim_hash,
Expand Down Expand Up @@ -174,3 +177,55 @@ fn test_remove_not_found() {
);
});
}

#[test]
fn test_remove_v1() {
let attester: AttesterOf<Test> = sr25519_did_from_public_key(&ALICE_SEED);
let claim_hash = claim_hash_from_seed(CLAIM_HASH_SEED_01);
let mut attestation = generate_base_attestation::<Test>(attester.clone(), ACCOUNT_00);
attestation.authorization_id = Some(attester.clone());
let ctype_hash = attestation.ctype_hash;

ExtBuilder::default()
.with_balances(vec![(ACCOUNT_00, <Test as Config>::Deposit::get() * 100)])
.with_ctypes(vec![(attestation.ctype_hash, attester.clone())])
.with_attestations_v1(vec![(claim_hash, attestation)])
.build_and_execute_with_sanity_tests(|| {
assert!(Attestation::attestations(claim_hash).is_some());
assert!(!ExternalAttestations::<Test>::get(attester.clone(), claim_hash));
assert!(migrations::v1::ExternalAttestations::<Test>::get(
attester.clone(),
claim_hash
));

assert_ok!(Attestation::remove(
DoubleOrigin(ACCOUNT_00, attester.clone()).into(),
claim_hash,
None
));
assert!(Attestation::attestations(claim_hash).is_none());
assert!(!ExternalAttestations::<Test>::get(attester.clone(), claim_hash));
assert!(!migrations::v1::ExternalAttestations::<Test>::get(
attester.clone(),
claim_hash
));

assert_eq!(
events(),
vec![
Event::AttestationRevoked {
attester: attester.clone(),
claim_hash,
authorized_by: attestation::authorized_by::AuthorizedBy::Attester(attester.clone()),
ctype_hash,
},
Event::AttestationRemoved {
attester: attester.clone(),
claim_hash,
authorized_by: attestation::authorized_by::AuthorizedBy::Attester(attester.clone()),
ctype_hash,
}
]
);
});
}
4 changes: 4 additions & 0 deletions pallets/did/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ pub mod pallet {
/// Service endpoints associated with DIDs.
///
/// It maps from (DID identifier, service ID) to the service details.
// SAFETY of Twox64Concat:
// The DID Identifier must be registered on chain first. To register a DID
// Identifier, you must provide a valid signature for the identifier or
// control the origin that corresponds to the identifier.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this play out with cross-chain origins? I know probably migration would be hard here, but it might still be worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DID that creates the endpoint still needs to be a valid accountId. If you would be able to choose an arbitrary accountId you could impersonate other accounts. So I don't think that this will ever be possible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but the resulting account ID is given by our location converter. There might be a way for a chain to craft origins which are then converted into something else on our chain. That would be the same as crafting origins on our chain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating specific AccountIds on our chain should never be possible? Even if the origin is coming from another chain.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not saying that. But to have an account on our chain you must have a valid private key to generate the signature. On a different chain, "trying" with different accounts might be cheaper, e.g., if, by absurd, you simply create a chain to spam all possible account IDs. This is probably very theoretical, but still possible. All those origins would be converted into some sort of account ID on our chain, but we don't really know what's the "proof of ownership" for that account ID on the source chain. Maybe this is somehow prevented by having XCM fees in place, which is also a mitigation in our case.

#[pallet::storage]
#[pallet::getter(fn get_service_endpoints)]
pub type ServiceEndpoints<T> =
Expand Down
13 changes: 11 additions & 2 deletions pallets/pallet-deposit-storage/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,19 @@ pub mod pallet {
/// Storage of all deposits. Its first key is a namespace, and the second
/// one the deposit key. Its value includes the details associated to a
/// deposit instance.
// SAFETY of Twox64Concat:
// The Namespace is not chosen by the user but by the pallet and therefore
// doesn't allow for arbitrary data.
#[pallet::storage]
#[pallet::getter(fn deposits)]
pub(crate) type Deposits<T> =
StorageDoubleMap<_, Twox64Concat, <T as Config>::Namespace, Twox64Concat, DepositKeyOf<T>, DepositEntryOf<T>>;
pub(crate) type Deposits<T> = StorageDoubleMap<
_,
Twox64Concat,
<T as Config>::Namespace,
Blake2_128Concat,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note this change. I think the DepositKey might be chosable by the user

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This follows the same reasoning as my other comments (not sure if above or below). The concrete type of a deposit key is defined by the runtime, but it could take user-specified values, so I am all in favor for this change.

DepositKeyOf<T>,
DepositEntryOf<T>,
>;

#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
Expand Down
4 changes: 3 additions & 1 deletion pallets/pallet-dip-consumer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ pub mod pallet {
dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo},
pallet_prelude::*,
traits::{Contains, EnsureOriginWithArg},
Twox64Concat,
};
use frame_system::pallet_prelude::*;
use parity_scale_codec::{FullCodec, MaxEncodedLen};
Expand Down Expand Up @@ -105,6 +104,9 @@ pub mod pallet {
/// The pallet contains a single storage element, the `IdentityEntries` map.
/// It maps from a subject `Identifier` to an instance of
/// `LocalIdentityInfo`.
// SAFETY of Twox64Concat:
// The Identifier cannot be chosen freely it is therefore not efficient to
// craft specific hashes.
ntn-x2 marked this conversation as resolved.
Show resolved Hide resolved
ntn-x2 marked this conversation as resolved.
Show resolved Hide resolved
#[pallet::storage]
#[pallet::getter(fn identity_proofs)]
pub(crate) type IdentityEntries<T> =
Expand Down
5 changes: 5 additions & 0 deletions pallets/pallet-dip-provider/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ pub mod pallet {
/// double map. Its first key is the `Identifier` of subjects, while the
/// second key is the commitment version. The values are identity
/// commitments.
// SAFETY of Twox64Concat:
ntn-x2 marked this conversation as resolved.
Show resolved Hide resolved
// The identifier MUST not be freely choosable. This must be ensured by the
// Config::IdentityProvider.
// The commitment version is a u16 and has only very limited value space
// which doesn't to exploit any collisions.
#[pallet::storage]
#[pallet::getter(fn identity_commitments)]
pub type IdentityCommitments<T> = StorageDoubleMap<
Expand Down
2 changes: 2 additions & 0 deletions pallets/pallet-relay-store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub mod pallet {

/// Maps from a relaychain block height to its related information,
/// including the state root.
// SAFETY of Twox64Concat:
// The key is the relaychain blocknumber. It cannot be chosen or set arbitrarily.
#[pallet::storage]
#[pallet::getter(fn latest_relay_head_for_block)]
pub(crate) type LatestRelayHeads<T: Config> = StorageMap<_, Twox64Concat, u32, RelayParentInfo<H256>>;
Expand Down
21 changes: 21 additions & 0 deletions pallets/parachain-staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,9 @@ pub mod pallet {
///
/// It maps from an account to the number of delegations in the last
/// session in which they (re-)delegated.
// SAFETY of Twox64Concat:
// The AccountId cannot be chosen freely since it's the signed origin of
// `join_delegators`.
#[pallet::storage]
#[pallet::getter(fn last_delegation)]
pub(crate) type LastDelegation<T: Config> =
Expand All @@ -560,6 +563,9 @@ pub mod pallet {
/// Delegation staking information.
///
/// It maps from an account to its delegation details.
// SAFETY of Twox64Concat:
// The AccountId cannot be chosen freely since it's the signed origin of
// `join_delegators`.
#[pallet::storage]
#[pallet::getter(fn delegator_state)]
pub(crate) type DelegatorState<T: Config> =
Expand All @@ -569,6 +575,9 @@ pub mod pallet {
///
/// It maps from an account to its information.
/// Moreover, it counts the number of candidates.
// SAFETY of Twox64Concat:
// The AccountId cannot be chosen freely since it's the signed origin of
// `join_candidates`.
#[pallet::storage]
#[pallet::getter(fn candidate_pool)]
pub(crate) type CandidatePool<T: Config> = CountedStorageMap<
Expand Down Expand Up @@ -612,6 +621,9 @@ pub mod pallet {
///
/// It maps from accounts to all the funds addressed to them in the future
/// blocks.
// SAFETY of Twox64Concat:
// The AccountId is either the Collator or Delegator account and therefore
// needs to be a valid signed origin. The key cannot be chosen arbitrarily.
#[pallet::storage]
#[pallet::getter(fn unstaking)]
pub(crate) type Unstaking<T: Config> = StorageMap<
Expand All @@ -638,6 +650,9 @@ pub mod pallet {

/// The number of authored blocks for collators. It is updated via the
/// `note_author` hook when authoring a block .
// SAFETY of Twox64Concat:
// The Account Id is the Id of a collator. It can only be set to a valid
// signed origin and cannot be chosen arbitrarily.
#[pallet::storage]
#[pallet::getter(fn blocks_authored)]
pub(crate) type BlocksAuthored<T: Config> =
Expand All @@ -652,6 +667,9 @@ pub mod pallet {
/// For delegators, this can be at most BlocksAuthored of the collator.It is
/// updated when incrementing delegator rewards, either when calling
/// `inc_delegator_rewards` or updating the `InflationInfo`.
// SAFETY of Twox64Concat:
// The Account Id is the Id of a collator. It can only be set to a valid
// signed origin and cannot be chosen arbitrarily.
#[pallet::storage]
#[pallet::getter(fn blocks_rewarded)]
pub(crate) type BlocksRewarded<T: Config> =
Expand All @@ -660,6 +678,9 @@ pub mod pallet {
/// The accumulated rewards for collator candidates and delegators.
///
/// It maps from accounts to their total rewards since the last payout.
// SAFETY of Twox64Concat:
// The Account Id is the Id of a collator or delegator. It can only be set
// to a valid signed origin and cannot be chosen arbitrarily.
#[pallet::storage]
#[pallet::getter(fn rewards)]
pub(crate) type Rewards<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, BalanceOf<T>, ValueQuery>;
Expand Down
2 changes: 1 addition & 1 deletion runtimes/clone/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

use cumulus_pallet_parachain_system::RelayNumberStrictlyIncreases;
use frame_support::PalletId;
use frame_support::{
construct_runtime, parameter_types,
traits::Everything,
weights::{ConstantMultiplier, Weight},
PalletId,
};
use frame_system::EnsureRoot;
use pallet_transaction_payment::CurrencyAdapter;
Expand Down
Loading