Skip to content

Commit 3ebd8a9

Browse files
committed
refactor: store both cip25 and cip68 metadata instead of overwriting
Signed-off-by: William Hankins <[email protected]>
1 parent 9886932 commit 3ebd8a9

File tree

3 files changed

+105
-78
lines changed

3 files changed

+105
-78
lines changed

common/src/types.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,8 +2121,15 @@ pub struct TxCertificateWithPos {
21212121
pub struct AssetInfoRecord {
21222122
pub initial_mint_tx: TxIdentifier,
21232123
pub mint_or_burn_count: u64,
2124-
pub onchain_metadata: Option<Vec<u8>>,
2125-
pub metadata_standard: Option<AssetMetadataStandard>,
2124+
pub metadata: AssetMetadata,
2125+
}
2126+
2127+
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
2128+
pub struct AssetMetadata {
2129+
pub cip25_metadata: Option<Vec<u8>>,
2130+
pub cip25_version: Option<AssetMetadataStandard>,
2131+
pub cip68_metadata: Option<Vec<u8>>,
2132+
pub cip68_version: Option<AssetMetadataStandard>,
21262133
}
21272134

21282135
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]

modules/assets_state/src/assets_state.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,18 @@ impl AssetsState {
443443
)),
444444
}
445445
}
446+
AssetsStateQuery::GetAssetsMetadata { assets } => {
447+
let reg = registry.lock().await;
448+
match state.get_assets_metadata(assets, &reg) {
449+
Ok(Some(assets)) => AssetsStateQueryResponse::AssetsMetadata(assets),
450+
Ok(None) => AssetsStateQueryResponse::Error(QueryError::not_found(
451+
"One or more assets not found in registry".to_string(),
452+
)),
453+
Err(e) => AssetsStateQueryResponse::Error(QueryError::internal_error(
454+
e.to_string(),
455+
)),
456+
}
457+
}
446458
};
447459
Arc::new(Message::StateQueryResponse(StateQueryResponse::Assets(
448460
response,

modules/assets_state/src/state.rs

Lines changed: 84 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use std::collections::HashSet;
55
use crate::asset_registry::{AssetId, AssetRegistry};
66
use acropolis_common::{
77
queries::assets::{AssetHistory, PolicyAssets},
8-
Address, AddressDelta, AssetAddressEntry, AssetInfoRecord, AssetMetadataStandard,
9-
AssetMintRecord, AssetName, Datum, Lovelace, NativeAssetsDelta, PolicyAsset, PolicyId,
10-
ShelleyAddress, TxIdentifier, TxUTxODeltas,
8+
Address, AddressDelta, AssetAddressEntry, AssetInfoRecord, AssetMetadata,
9+
AssetMetadataStandard, AssetMintRecord, AssetName, Datum, Lovelace, NativeAssets,
10+
NativeAssetsDelta, PolicyAsset, PolicyId, ShelleyAddress, TxIdentifier, TxUTxODeltas,
1111
};
1212
use anyhow::Result;
1313
use imbl::{HashMap, Vector};
@@ -143,8 +143,8 @@ impl State {
143143
// Overwrite asset metadata if an associated CIP68 reference token is found
144144
if let Some(ref_info) = self.resolve_cip68_metadata(asset_id, registry) {
145145
if let Some(info_mut) = info.as_mut() {
146-
info_mut.onchain_metadata = ref_info.onchain_metadata;
147-
info_mut.metadata_standard = ref_info.metadata_standard;
146+
info_mut.metadata.cip68_metadata = ref_info.metadata.cip68_metadata;
147+
info_mut.metadata.cip68_version = ref_info.metadata.cip68_version;
148148
} else {
149149
info = Some(ref_info);
150150
}
@@ -240,6 +240,44 @@ impl State {
240240
Ok(Some(result))
241241
}
242242

243+
pub fn get_assets_metadata(
244+
&self,
245+
assets: &NativeAssets,
246+
registry: &AssetRegistry,
247+
) -> Result<Option<Vec<AssetMetadata>>> {
248+
if !self.config.store_info || !self.config.store_assets {
249+
return Err(anyhow::anyhow!("asset info storage disabled in config"));
250+
}
251+
252+
let mut out = Vec::new();
253+
254+
for (policy_id, policy_assets) in assets {
255+
for asset in policy_assets {
256+
let asset_id = match registry.lookup_id(policy_id, &asset.name) {
257+
Some(id) => id,
258+
None => {
259+
return Ok(None);
260+
}
261+
};
262+
263+
let info = match self.info.as_ref().and_then(|map| map.get(&asset_id)) {
264+
Some(rec) => rec,
265+
None => {
266+
return Err(anyhow::anyhow!(
267+
"asset info missing in state for {}:{}",
268+
hex::encode(policy_id),
269+
hex::encode(asset.name.as_slice())
270+
));
271+
}
272+
};
273+
274+
out.push(info.metadata.clone());
275+
}
276+
}
277+
278+
Ok(Some(out))
279+
}
280+
243281
pub fn tick(&self) -> Result<()> {
244282
if let Some(supply) = &self.supply {
245283
self.log_assets(supply.len());
@@ -312,8 +350,12 @@ impl State {
312350
.or_insert(AssetInfoRecord {
313351
initial_mint_tx: *tx_identifier,
314352
mint_or_burn_count: 1,
315-
onchain_metadata: None,
316-
metadata_standard: None,
353+
metadata: AssetMetadata {
354+
cip25_metadata: None,
355+
cip25_version: None,
356+
cip68_metadata: None,
357+
cip68_version: None,
358+
},
317359
});
318360
}
319361

@@ -525,8 +567,8 @@ impl State {
525567
if let Some(asset_id) = registry.lookup_id(&policy_id, &asset_name) {
526568
if let Ok(metadata_raw) = serde_cbor::to_vec(&metadata_val) {
527569
if let Some(record) = info_map.get_mut(&asset_id) {
528-
record.onchain_metadata = Some(metadata_raw);
529-
record.metadata_standard = Some(standard);
570+
record.metadata.cip25_metadata = Some(metadata_raw);
571+
record.metadata.cip25_version = Some(standard);
530572
}
531573
}
532574
}
@@ -554,6 +596,21 @@ impl State {
554596
continue;
555597
};
556598

599+
let mut cip68_version = None;
600+
601+
if let Ok(serde_cbor::Value::Map(m)) =
602+
serde_cbor::from_slice::<serde_cbor::Value>(blob)
603+
{
604+
let version_key = serde_cbor::Value::Text("version".to_string());
605+
606+
if let Some(serde_cbor::Value::Text(ver)) = m.get(&version_key) {
607+
cip68_version = match ver.as_str() {
608+
"2.0" => Some(AssetMetadataStandard::CIP68v2),
609+
_ => Some(AssetMetadataStandard::CIP68v1),
610+
};
611+
}
612+
}
613+
557614
for (policy_id, native_assets) in &output.value.assets {
558615
for asset in native_assets {
559616
let name = &asset.name;
@@ -568,7 +625,8 @@ impl State {
568625
if let Some(record) =
569626
new_info.as_mut().and_then(|m| m.get_mut(&asset_id))
570627
{
571-
record.onchain_metadata = Some(blob.clone());
628+
record.metadata.cip68_metadata = Some(blob.clone());
629+
record.metadata.cip68_version = cip68_version;
572630
}
573631
}
574632
None => {
@@ -607,8 +665,8 @@ impl State {
607665
match label {
608666
CIP68_LABEL_100 => self.info.as_ref()?.get(asset_id).cloned().map(|mut rec| {
609667
// Hide metadata on the reference itself (Per Blockfrost spec)
610-
rec.onchain_metadata = None;
611-
rec.metadata_standard = None;
668+
rec.metadata.cip68_metadata = None;
669+
rec.metadata.cip68_version = None;
612670
rec
613671
}),
614672

@@ -998,10 +1056,10 @@ mod tests {
9981056
let record = info.get(&asset_id).unwrap();
9991057

10001058
// Onchain metadata has been set
1001-
assert!(record.onchain_metadata.is_some());
1059+
assert!(record.metadata.cip25_metadata.is_some());
10021060
// Metadata standard defaults to v1 if not present in map
10031061
assert_eq!(
1004-
record.metadata_standard,
1062+
record.metadata.cip25_version,
10051063
Some(AssetMetadataStandard::CIP25v1)
10061064
);
10071065
}
@@ -1028,10 +1086,10 @@ mod tests {
10281086
let record = info.get(&asset_id).unwrap();
10291087

10301088
// Onchain metadata has been set
1031-
assert!(record.onchain_metadata.is_some());
1089+
assert!(record.metadata.cip25_metadata.is_some());
10321090
// Metadata standard set to v2 when present in map
10331091
assert_eq!(
1034-
record.metadata_standard,
1092+
record.metadata.cip25_version,
10351093
Some(AssetMetadataStandard::CIP25v2)
10361094
);
10371095
}
@@ -1059,7 +1117,7 @@ mod tests {
10591117

10601118
// Metadata for known asset unchanged by unknown asset
10611119
assert!(
1062-
record.onchain_metadata.is_none(),
1120+
record.metadata.cip25_metadata.is_none(),
10631121
"unknown asset should not update records"
10641122
);
10651123
}
@@ -1085,12 +1143,12 @@ mod tests {
10851143

10861144
// Metadata not set when CBOR is invalid
10871145
assert!(
1088-
record.onchain_metadata.is_none(),
1146+
record.metadata.cip25_metadata.is_none(),
10891147
"invalid CBOR should be ignored"
10901148
);
10911149
// Metadata standard not set when CBOR is invalid
10921150
assert!(
1093-
record.metadata_standard.is_none(),
1151+
record.metadata.cip25_version.is_none(),
10941152
"invalid CBOR should not set a standard"
10951153
);
10961154
}
@@ -1124,7 +1182,7 @@ mod tests {
11241182
let record = info.get(&reference_id).expect("record should exist");
11251183

11261184
// Onchain metadata set when asset already exists and TxOutput with inline datum is processed
1127-
assert_eq!(record.onchain_metadata, Some(datum_blob));
1185+
assert_eq!(record.metadata.cip68_metadata, Some(datum_blob));
11281186
}
11291187

11301188
#[test]
@@ -1156,7 +1214,7 @@ mod tests {
11561214
let record = info.get(&normal_id).expect("non reference asset should exist");
11571215

11581216
// Onchain metadata not updated for non reference asset
1159-
assert_eq!(record.onchain_metadata, None);
1217+
assert_eq!(record.metadata.cip68_metadata, None);
11601218
}
11611219

11621220
#[test]
@@ -1223,7 +1281,7 @@ mod tests {
12231281

12241282
// Metadata not populated for inputs or outputs without inline datum
12251283
assert!(
1226-
record.onchain_metadata.is_none(),
1284+
record.metadata.cip68_metadata.is_none(),
12271285
"inputs and outputs without datums should both be ignored"
12281286
);
12291287
}
@@ -1244,8 +1302,8 @@ mod tests {
12441302

12451303
let mut info = state.info.take().unwrap();
12461304
let rec = info.get_mut(&ref_id).unwrap();
1247-
rec.onchain_metadata = Some(vec![1, 2, 3]);
1248-
rec.metadata_standard = Some(AssetMetadataStandard::CIP68v1);
1305+
rec.metadata.cip68_metadata = Some(vec![1, 2, 3]);
1306+
rec.metadata.cip68_version = Some(AssetMetadataStandard::CIP68v1);
12491307
state.info = Some(info);
12501308

12511309
state.supply = Some(imbl::HashMap::new());
@@ -1257,59 +1315,9 @@ mod tests {
12571315
// Supply unchanged
12581316
assert_eq!(supply, 42);
12591317
// Metadata removed for reference asset
1260-
assert!(rec.onchain_metadata.is_none());
1318+
assert!(rec.metadata.cip68_metadata.is_none());
12611319
// Metadata standard removed for reference asset
1262-
assert!(rec.metadata_standard.is_none());
1263-
}
1264-
1265-
#[test]
1266-
fn resolve_cip68_metadata_overwrites_cip25_user_token_metadata() {
1267-
let mut registry = AssetRegistry::new();
1268-
let policy_id: PolicyId = [10u8; 28];
1269-
1270-
let user_name = AssetName::new(&[0x00, 0x0d, 0xe1, 0x40, 0xaa]).unwrap();
1271-
let user_id = registry.get_or_insert(policy_id, user_name);
1272-
1273-
let mut ref_bytes = user_name.as_slice().to_vec();
1274-
ref_bytes[0..4].copy_from_slice(&[0x00, 0x06, 0x43, 0xb0]);
1275-
let ref_name = AssetName::new(&ref_bytes).unwrap();
1276-
let ref_id = registry.get_or_insert(policy_id, ref_name);
1277-
1278-
let mut state = State::new(AssetsStorageConfig {
1279-
store_info: true,
1280-
store_assets: true,
1281-
..Default::default()
1282-
});
1283-
let mut info_map = imbl::HashMap::new();
1284-
1285-
let user_record = AssetInfoRecord {
1286-
onchain_metadata: Some(vec![1, 2, 3]),
1287-
metadata_standard: Some(AssetMetadataStandard::CIP25v1),
1288-
..Default::default()
1289-
};
1290-
info_map.insert(user_id, user_record);
1291-
1292-
let ref_record = AssetInfoRecord {
1293-
onchain_metadata: Some(vec![9, 9, 9]),
1294-
metadata_standard: Some(AssetMetadataStandard::CIP68v2),
1295-
..Default::default()
1296-
};
1297-
info_map.insert(ref_id, ref_record);
1298-
1299-
state.info = Some(info_map);
1300-
1301-
state.supply = Some(imbl::HashMap::new());
1302-
state.supply.as_mut().unwrap().insert(user_id, 100);
1303-
1304-
let result = state.get_asset_info(&user_id, &registry).unwrap().unwrap();
1305-
let (supply, rec) = result;
1306-
1307-
// User asset supply unchanged
1308-
assert_eq!(supply, 100);
1309-
// User asset metadata overwritten with reference token metadata
1310-
assert_eq!(rec.onchain_metadata, Some(vec![9, 9, 9]));
1311-
// User asset metadata standard overwritten with reference token metadata standard
1312-
assert_eq!(rec.metadata_standard, Some(AssetMetadataStandard::CIP68v2));
1320+
assert!(rec.metadata.cip68_version.is_none());
13131321
}
13141322

13151323
#[test]

0 commit comments

Comments
 (0)