Tokens pallet (#243)

* Use Monero-compatible additional TX keys

This still sends a fingerprinting flare up if you send to a subaddress which
needs to be fixed. Despite that, Monero no should no longer fail to scan TXs
from monero-serai regarding additional keys.

Previously it failed becuase we supplied one key as THE key, and n-1 as
additional. Monero expects n for additional.

This does correctly select when to use THE key versus when to use the additional
key when sending. That removes the ability for recipients to fingerprint
monero-serai by receiving to a standard address yet needing to use an additional
key.

* Add tokens_primitives

Moves OutInstruction from in-instructions.

Turns Destination into OutInstruction.

* Correct in-instructions DispatchClass

* Add initial tokens pallet

* Don't allow pallet addresses to equal identity

* Add support for InInstruction::transfer

Requires a cargo update due to modifications made to serai-dex/substrate.

Successfully mints a token to a SeraiAddress.

* Bind InInstructions to an amount

* Add a call filter to the runtime

Prevents worrying about calls to the assets pallet/generally tightens things
up.

* Restore Destination

It was meged into OutInstruction, yet it didn't make sense for OutInstruction
to contain a SeraiAddress.

Also deletes the excessively dated Scenarios doc.

* Split PublicKey/SeraiAddress

Lets us define a custom Display/ToString for SeraiAddress.

Also resolves an oddity where PublicKey would be encoded as String, not
[u8; 32].

* Test burning tokens/retrieving OutInstructions

Modularizes processor_coinUpdates into a shared testing utility.

* Misc lint

* Don't use PolkadotExtrinsicParams
This commit is contained in:
Luke Parker
2023-01-28 01:47:13 -05:00
committed by GitHub
parent f12cc2cca6
commit 2ace339975
39 changed files with 1213 additions and 594 deletions

View File

@@ -1,43 +1,42 @@
use sp_core::{Decode, Pair as PairTrait, sr25519::Pair};
use sp_runtime::traits::TrailingZeroInput;
use sp_core::Pair as PairTrait;
use sc_service::ChainType;
use serai_runtime::{
primitives::*, tendermint::crypto::Public, WASM_BINARY, opaque::SessionKeys, GenesisConfig,
SystemConfig, BalancesConfig, AssetsConfig, ValidatorSetsConfig, SessionConfig,
primitives::*, tokens::primitives::ADDRESS as TOKENS_ADDRESS, tendermint::crypto::Public,
WASM_BINARY, opaque::SessionKeys, GenesisConfig, SystemConfig, BalancesConfig, AssetsConfig,
ValidatorSetsConfig, SessionConfig,
};
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;
fn insecure_pair_from_name(name: &'static str) -> Pair {
Pair::from_string(&format!("//{name}"), None).unwrap()
}
fn address_from_name(name: &'static str) -> SeraiAddress {
fn account_from_name(name: &'static str) -> PublicKey {
insecure_pair_from_name(name).public()
}
fn testnet_genesis(
wasm_binary: &[u8],
validators: &[&'static str],
endowed_accounts: Vec<SeraiAddress>,
endowed_accounts: Vec<PublicKey>,
) -> GenesisConfig {
let session_key = |name| {
let key = address_from_name(name);
let key = account_from_name(name);
(key, key, SessionKeys { tendermint: Public::from(key) })
};
// TODO: Replace with a call to the pallet to ask for its account
let owner = SeraiAddress::decode(&mut TrailingZeroInput::new(b"tokens")).unwrap();
GenesisConfig {
system: SystemConfig { code: wasm_binary.to_vec() },
balances: BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, 1 << 60)).collect(),
},
transaction_payment: Default::default(),
assets: AssetsConfig {
assets: [BITCOIN, ETHER, DAI, MONERO].iter().map(|coin| (*coin, owner, true, 1)).collect(),
assets: [BITCOIN, ETHER, DAI, MONERO]
.iter()
.map(|coin| (*coin, TOKENS_ADDRESS.into(), true, 1))
.collect(),
metadata: vec![
(BITCOIN, b"Bitcoin".to_vec(), b"BTC".to_vec(), 8),
// Reduce to 8 decimals to feasibly fit within u64 (instead of its native u256)
@@ -47,14 +46,13 @@ fn testnet_genesis(
],
accounts: vec![],
},
transaction_payment: Default::default(),
validator_sets: ValidatorSetsConfig {
bond: Amount(1_000_000) * COIN,
coins: vec![BITCOIN, ETHER, DAI, MONERO],
participants: validators.iter().map(|name| address_from_name(name)).collect(),
},
session: SessionConfig { keys: validators.iter().map(|name| session_key(*name)).collect() },
validator_sets: ValidatorSetsConfig {
bond: Amount(1_000_000 * 10_u64.pow(8)),
coins: vec![BITCOIN, ETHER, DAI, MONERO],
participants: validators.iter().map(|name| account_from_name(name)).collect(),
},
}
}
@@ -72,18 +70,18 @@ pub fn development_config() -> Result<ChainSpec, &'static str> {
wasm_binary,
&["Alice"],
vec![
address_from_name("Alice"),
address_from_name("Bob"),
address_from_name("Charlie"),
address_from_name("Dave"),
address_from_name("Eve"),
address_from_name("Ferdie"),
address_from_name("Alice//stash"),
address_from_name("Bob//stash"),
address_from_name("Charlie//stash"),
address_from_name("Dave//stash"),
address_from_name("Eve//stash"),
address_from_name("Ferdie//stash"),
account_from_name("Alice"),
account_from_name("Bob"),
account_from_name("Charlie"),
account_from_name("Dave"),
account_from_name("Eve"),
account_from_name("Ferdie"),
account_from_name("Alice//stash"),
account_from_name("Bob//stash"),
account_from_name("Charlie//stash"),
account_from_name("Dave//stash"),
account_from_name("Eve//stash"),
account_from_name("Ferdie//stash"),
],
)
},
@@ -116,18 +114,18 @@ pub fn testnet_config() -> Result<ChainSpec, &'static str> {
wasm_binary,
&["Alice", "Bob", "Charlie"],
vec![
address_from_name("Alice"),
address_from_name("Bob"),
address_from_name("Charlie"),
address_from_name("Dave"),
address_from_name("Eve"),
address_from_name("Ferdie"),
address_from_name("Alice//stash"),
address_from_name("Bob//stash"),
address_from_name("Charlie//stash"),
address_from_name("Dave//stash"),
address_from_name("Eve//stash"),
address_from_name("Ferdie//stash"),
account_from_name("Alice"),
account_from_name("Bob"),
account_from_name("Charlie"),
account_from_name("Dave"),
account_from_name("Eve"),
account_from_name("Ferdie"),
account_from_name("Alice//stash"),
account_from_name("Bob//stash"),
account_from_name("Charlie//stash"),
account_from_name("Dave//stash"),
account_from_name("Eve//stash"),
account_from_name("Ferdie//stash"),
],
)
},

View File

@@ -67,7 +67,7 @@ pub fn create_benchmark_extrinsic(
UncheckedExtrinsic::new_signed(
call.clone(),
sender.public(),
sender.public().into(),
SignedPayload::from_raw(
call,
extra.clone(),

View File

@@ -6,7 +6,11 @@ use sp_blockchain::{Error as BlockchainError, HeaderBackend, HeaderMetadata};
use sp_block_builder::BlockBuilder;
use sp_api::ProvideRuntimeApi;
use serai_runtime::{primitives::SeraiAddress, opaque::Block, Balance, Index};
use serai_runtime::{
primitives::{SubstrateAmount, PublicKey},
opaque::Block,
Index,
};
pub use sc_rpc_api::DenyUnsafe;
use sc_transaction_pool_api::TransactionPool;
@@ -29,8 +33,8 @@ pub fn create_full<
deps: FullDeps<C, P>,
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
where
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, SeraiAddress, Index>
+ pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, PublicKey, Index>
+ pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, SubstrateAmount>
+ BlockBuilder<Block>,
{
use substrate_frame_rpc_system::{System, SystemApiServer};

View File

@@ -76,9 +76,9 @@ impl TendermintClientMinimal for TendermintValidatorFirm {
// guaranteed not to grow the block?
const PROPOSED_BLOCK_SIZE_LIMIT: usize = { BLOCK_SIZE as usize };
// 3 seconds
const BLOCK_PROCESSING_TIME_IN_SECONDS: u32 = { (TARGET_BLOCK_TIME / 2 / 1000) as u32 };
const BLOCK_PROCESSING_TIME_IN_SECONDS: u32 = { (TARGET_BLOCK_TIME / 2) as u32 };
// 1 second
const LATENCY_TIME_IN_SECONDS: u32 = { (TARGET_BLOCK_TIME / 2 / 3 / 1000) as u32 };
const LATENCY_TIME_IN_SECONDS: u32 = { (TARGET_BLOCK_TIME / 2 / 3) as u32 };
type Block = Block;
type Backend = sc_client_db::Backend<Block>;
@@ -101,7 +101,7 @@ impl TendermintValidator for TendermintValidatorFirm {
pub fn new_partial(
config: &Configuration,
) -> Result<(TendermintImport<TendermintValidatorFirm>, PartialComponents), ServiceError> {
debug_assert_eq!(TARGET_BLOCK_TIME, 6000);
debug_assert_eq!(TARGET_BLOCK_TIME, 6);
if config.keystore_remote.is_some() {
return Err(ServiceError::Other("Remote Keystores are not supported".to_string()));