Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/evd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ fn run_custom_genesis<S: ReadonlyKV + Storage>(
(addr.into_array(), acc.balance)
})
.collect();
let minter = AccountId::new(genesis_config.minter_id);
let minter = AccountId::from_u64(genesis_config.minter_id);
let metadata = genesis_config.token.to_metadata();

let gas_config = default_gas_config();
Expand Down
2 changes: 1 addition & 1 deletion bin/testapp/src/genesis_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub type FundedAccount = ([u8; 20], u128);
#[derive(Deserialize)]
pub struct EvdGenesisConfig {
pub token: TokenConfig,
pub minter_id: u128,
pub minter_id: u64,
pub accounts: Vec<AccountConfig>,
}

Expand Down
4 changes: 2 additions & 2 deletions bin/testapp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use evolve_token::account::{Token, TokenRef};
use evolve_tx_eth::TxContext;
use evolve_tx_eth::{register_runtime_contract_account, resolve_or_create_eoa_account};

pub const MINTER: AccountId = AccountId::new(100_002);
pub const MINTER: AccountId = AccountId::from_u64(100002);

pub struct MempoolNoOpPostTx;

Expand All @@ -42,7 +42,7 @@ pub type MempoolStf = Stf<
MempoolNoOpPostTx,
>;

pub const PLACEHOLDER_ACCOUNT: AccountId = AccountId::new(u128::MAX);
pub const PLACEHOLDER_ACCOUNT: AccountId = AccountId::from_u64(u64::MAX);

/// Default gas configuration for the test application.
pub fn default_gas_config() -> StorageGasConfig {
Expand Down
2 changes: 1 addition & 1 deletion bin/testapp/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ fn run_custom_genesis<S: ReadonlyKV + Storage>(
return Err("custom genesis requires at least one account".into());
}

let minter = AccountId::new(config.minter_id);
let minter = AccountId::from_u64(config.minter_id);
let metadata = config.token.to_metadata();

let genesis_block = BlockContext::new(0, 0);
Expand Down
2 changes: 1 addition & 1 deletion bin/testapp/tests/mempool_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ fn setup_genesis(

let init_storage = AsyncMockStorage::new();
let gas_config = default_gas_config();
let stf = build_mempool_stf(gas_config.clone(), AccountId::new(0));
let stf = build_mempool_stf(gas_config.clone(), AccountId::from_u64(0));

let (genesis_state, genesis_accounts) = do_eth_genesis(
&stf,
Expand Down
2 changes: 1 addition & 1 deletion bin/txload/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ mod tests {

#[test]
fn build_transfer_calldata_has_selector_and_borsh_args() {
let recipient = AccountId::new(42);
let recipient = AccountId::from_u64(42);
let amount = 999_u128;

let calldata = build_transfer_calldata(recipient, amount).unwrap();
Expand Down
1 change: 1 addition & 0 deletions crates/app/genesis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ evolve_stf = { workspace = true }
evolve_stf_traits = { workspace = true }

borsh = { workspace = true }
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
Expand Down
173 changes: 116 additions & 57 deletions crates/app/genesis/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct GenesisTxJson {
pub id: Option<String>,

/// Sender specification.
/// - "system": Use the system account (AccountId::new(0))
/// - "system": Use the system account (AccountId::from_u64(0))
/// - "$ref": Reference a previously created account
/// - number: Use a specific account ID
#[serde(default = "default_sender")]
Expand All @@ -64,15 +64,25 @@ fn default_sender() -> SenderSpec {
}

/// Specification for the sender of a genesis transaction.
#[derive(Debug, Clone, Default, Serialize)]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum SenderSpec {
/// Use the system account
#[default]
System,
/// Reference a previously created account (starts with $)
Reference(String),
/// Use a specific account ID
AccountId(u128),
AccountId(AccountId),
}

impl Serialize for SenderSpec {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
SenderSpec::System => serializer.serialize_str("system"),
SenderSpec::Reference(r) => serializer.serialize_str(r),
SenderSpec::AccountId(id) => serializer.serialize_str(&hex::encode(id.to_bytes())),
}
}
}

impl<'de> serde::Deserialize<'de> for SenderSpec {
Expand All @@ -85,26 +95,47 @@ impl<'de> serde::Deserialize<'de> for SenderSpec {
let value = serde_json::Value::deserialize(deserializer)?;
match value {
serde_json::Value::String(s) if s == "system" => Ok(SenderSpec::System),
serde_json::Value::String(s) => Ok(SenderSpec::Reference(s)),
serde_json::Value::Number(n) => n
.as_u64()
.map(|v| SenderSpec::AccountId(v as u128))
.or_else(|| n.as_i64().map(|v| SenderSpec::AccountId(v as u128)))
.ok_or_else(|| D::Error::custom("invalid account ID number")),
serde_json::Value::String(s) if s.starts_with('$') => Ok(SenderSpec::Reference(s)),
serde_json::Value::String(s) => {
// Try hex-encoded account ID, fall back to reference
match hex::decode(s.trim_start_matches("0x")) {
Ok(b) if b.len() == 32 => {
let bytes: [u8; 32] = b.try_into().unwrap();
Ok(SenderSpec::AccountId(AccountId::from_bytes(bytes)))
}
_ => Ok(SenderSpec::Reference(s)),
}
}
serde_json::Value::Number(n) => {
let v = n
.as_u64()
.ok_or_else(|| D::Error::custom("invalid account ID number"))?;
Ok(SenderSpec::AccountId(AccountId::from_u64(v)))
}
_ => Err(D::Error::custom("expected string or number for sender")),
}
}
}

/// Specification for the recipient of a genesis transaction.
#[derive(Debug, Clone, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RecipientSpec {
/// Use the runtime account for account creation
Runtime,
/// Reference a previously created account (starts with $)
Reference(String),
/// Use a specific account ID
AccountId(u128),
AccountId(AccountId),
}

impl Serialize for RecipientSpec {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
RecipientSpec::Runtime => serializer.serialize_str("runtime"),
RecipientSpec::Reference(r) => serializer.serialize_str(r),
RecipientSpec::AccountId(id) => serializer.serialize_str(&hex::encode(id.to_bytes())),
}
}
}

impl<'de> serde::Deserialize<'de> for RecipientSpec {
Expand All @@ -117,12 +148,23 @@ impl<'de> serde::Deserialize<'de> for RecipientSpec {
let value = serde_json::Value::deserialize(deserializer)?;
match value {
serde_json::Value::String(s) if s == "runtime" => Ok(RecipientSpec::Runtime),
serde_json::Value::String(s) => Ok(RecipientSpec::Reference(s)),
serde_json::Value::Number(n) => n
.as_u64()
.map(|v| RecipientSpec::AccountId(v as u128))
.or_else(|| n.as_i64().map(|v| RecipientSpec::AccountId(v as u128)))
.ok_or_else(|| D::Error::custom("invalid account ID number")),
serde_json::Value::String(s) if s.starts_with('$') => Ok(RecipientSpec::Reference(s)),
serde_json::Value::String(s) => {
// Try hex-encoded account ID, fall back to reference
match hex::decode(s.trim_start_matches("0x")) {
Ok(b) if b.len() == 32 => {
let bytes: [u8; 32] = b.try_into().unwrap();
Ok(RecipientSpec::AccountId(AccountId::from_bytes(bytes)))
}
_ => Ok(RecipientSpec::Reference(s)),
}
}
serde_json::Value::Number(n) => {
let v = n
.as_u64()
.ok_or_else(|| D::Error::custom("invalid account ID number"))?;
Ok(RecipientSpec::AccountId(AccountId::from_u64(v)))
}
_ => Err(D::Error::custom("expected string or number for recipient")),
}
}
Expand Down Expand Up @@ -189,27 +231,31 @@ impl GenesisFile {

let mut txs = Vec::with_capacity(self.transactions.len());
let mut id_to_account: HashMap<String, AccountId> = HashMap::new();
let mut next_account_id: u128 = 1; // Start from 1, 0 is system
// Start from 1, 0 is system
let mut next_account_id = AccountId::from_u64(1);

for (idx, tx_json) in self.transactions.iter().enumerate() {
// Resolve sender
let sender =
match &tx_json.sender {
SenderSpec::System => SYSTEM_ACCOUNT_ID,
SenderSpec::Reference(r) if r.starts_with('$') => {
let ref_id = &r[1..];
*id_to_account.get(ref_id).ok_or_else(|| {
GenesisError::InvalidReference(ref_id.to_string(), idx)
})?
}
SenderSpec::Reference(r) => {
// Treat as account ID string
AccountId::new(r.parse().map_err(|_| {
let sender = match &tx_json.sender {
SenderSpec::System => SYSTEM_ACCOUNT_ID,
SenderSpec::Reference(r) if r.starts_with('$') => {
let ref_id = &r[1..];
*id_to_account
.get(ref_id)
.ok_or_else(|| GenesisError::InvalidReference(ref_id.to_string(), idx))?
}
SenderSpec::Reference(r) => {
// Treat as hex-encoded 32-byte account ID
let bytes = hex::decode(r.trim_start_matches("0x"))
.ok()
.and_then(|b| <[u8; 32]>::try_from(b).ok())
.ok_or_else(|| {
GenesisError::ParseError(format!("invalid sender: {}", r))
})?)
}
SenderSpec::AccountId(id) => AccountId::new(*id),
};
})?;
AccountId::from_bytes(bytes)
}
SenderSpec::AccountId(id) => *id,
};

// Resolve recipient
let recipient = match &tx_json.recipient {
Expand All @@ -221,12 +267,16 @@ impl GenesisFile {
.ok_or_else(|| GenesisError::InvalidReference(ref_id.to_string(), idx))?
}
RecipientSpec::Reference(r) => {
// Treat as account ID string
AccountId::new(r.parse().map_err(|_| {
GenesisError::ParseError(format!("invalid recipient: {}", r))
})?)
// Treat as hex-encoded 32-byte account ID
let bytes = hex::decode(r.trim_start_matches("0x"))
.ok()
.and_then(|b| <[u8; 32]>::try_from(b).ok())
.ok_or_else(|| {
GenesisError::ParseError(format!("invalid recipient: {}", r))
})?;
AccountId::from_bytes(bytes)
}
RecipientSpec::AccountId(id) => AccountId::new(*id),
RecipientSpec::AccountId(id) => *id,
};

// Resolve $references in the message payload
Expand All @@ -244,9 +294,8 @@ impl GenesisFile {
// If this transaction has an ID, assign it the next account ID
// This assumes the transaction creates an account
if let Some(id) = &tx_json.id {
let account_id = AccountId::new(next_account_id);
id_to_account.insert(id.clone(), account_id);
next_account_id += 1;
id_to_account.insert(id.clone(), next_account_id);
next_account_id = next_account_id.increase();
}

txs.push(genesis_tx);
Expand All @@ -267,8 +316,9 @@ fn resolve_references_in_value(
let account_id = id_to_account
.get(ref_id)
.ok_or_else(|| GenesisError::InvalidReference(ref_id.to_string(), 0))?;
// Use string representation for u128 since JSON numbers are limited to i64/u64
Ok(serde_json::Value::String(account_id.inner().to_string()))
Ok(serde_json::Value::String(hex::encode(
account_id.as_bytes(),
)))
}
serde_json::Value::Array(arr) => {
let resolved: Result<Vec<_>, _> = arr
Expand Down Expand Up @@ -360,7 +410,7 @@ mod tests {
#[test]
fn test_resolve_references_in_value() {
let mut id_to_account = HashMap::new();
id_to_account.insert("alice".to_string(), AccountId::new(1));
id_to_account.insert("alice".to_string(), AccountId::from_u64(1));

let value = serde_json::json!({
"account": "$alice",
Expand All @@ -372,10 +422,19 @@ mod tests {

let resolved = resolve_references_in_value(&value, &id_to_account).unwrap();

// Account IDs are resolved as strings (for u128 compatibility)
assert_eq!(resolved["account"], "1");
assert_eq!(resolved["nested"]["ref"], "1");
assert_eq!(resolved["list"][0], "1");
// Account IDs are resolved as hex strings
assert_eq!(
resolved["account"],
"0000000000000000000000000000000000000000000000000000000000000001"
);
assert_eq!(
resolved["nested"]["ref"],
"0000000000000000000000000000000000000000000000000000000000000001"
);
assert_eq!(
resolved["list"][0],
"0000000000000000000000000000000000000000000000000000000000000001"
);
assert_eq!(resolved["list"][1], "literal");
}

Expand Down Expand Up @@ -459,10 +518,10 @@ mod tests {
]
}"#;
let genesis = GenesisFile::parse(json).unwrap();
assert!(matches!(
assert_eq!(
genesis.transactions[0].sender,
SenderSpec::AccountId(123)
));
SenderSpec::AccountId(AccountId::from_u64(123))
);
}

#[test]
Expand All @@ -488,10 +547,10 @@ mod tests {
]
}"#;
let genesis = GenesisFile::parse(json).unwrap();
assert!(matches!(
assert_eq!(
genesis.transactions[0].recipient,
RecipientSpec::AccountId(456)
));
RecipientSpec::AccountId(AccountId::from_u64(456))
);
}

#[test]
Expand Down Expand Up @@ -577,8 +636,8 @@ mod tests {
let txs = genesis.to_transactions(&registry).unwrap();

assert_eq!(txs.len(), 3);
assert_eq!(txs[2].sender, AccountId::new(1));
assert_eq!(txs[2].recipient, AccountId::new(1));
assert_eq!(txs[2].sender, AccountId::from_u64(1));
assert_eq!(txs[2].recipient, AccountId::from_u64(1));
}

#[test]
Expand Down
Loading
Loading