From d2b276d57bf338163a1881ba5385d94ecf77244c Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 16 Nov 2023 13:11:39 +0100 Subject: [PATCH 01/12] iface: add convenience AssetTag constructors --- src/interface/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 8ec4554a..2f1180ec 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -32,6 +32,7 @@ pub mod rgb21; pub mod rgb25; mod suppl; +pub use asset_tag_ext::AssetTagExt; pub use builder::{BuilderError, ContractBuilder, TransitionBuilder}; pub use contract::{ AllocationWitness, ContractIface, FilterExclude, FilterIncludeAll, FungibleAllocation, @@ -47,6 +48,8 @@ pub use rgb21::{rgb21, rgb21_stl, Rgb21, LIB_ID_RGB21, LIB_NAME_RGB21}; pub use rgb25::{rgb25, rgb25_stl, Rgb25, LIB_ID_RGB25, LIB_NAME_RGB25}; pub use suppl::{ContractSuppl, OwnedStateSuppl, SupplId, TickerSuppl, VelocityHint}; +use crate::stl::Ticker; + #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Default)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = crate::LIB_NAME_RGB_STD, tags = repr, into_u8, try_from_u8)] @@ -62,3 +65,40 @@ pub enum VerNo { #[display("v1")] V1 = 0, } + +mod asset_tag_ext { + use std::time::SystemTime; + + use amplify::confinement::U8; + use bp::secp256k1::rand::{thread_rng, RngCore}; + use commit_verify::{DigestExt, Sha256}; + use rgb::AssetTag; + + use super::*; + + pub trait AssetTagExt: Sized { + fn new_rgb20(issuer_domain: &str, ticker: &Ticker) -> Self { + Self::new_custom("RGB20", &format!("{issuer_domain}/{ticker}")) + } + fn new_rgb21(issuer_domain: &str, ticker: &Ticker) -> Self { + Self::new_custom("RGB21", &format!("{issuer_domain}/{ticker}")) + } + fn new_rgb25(issuer_domain: &str, ticker: &Ticker) -> Self { + Self::new_custom("RGB25", &format!("{issuer_domain}/{ticker}")) + } + fn new_custom(iface_name: &str, data: &str) -> Self; + } + + impl AssetTagExt for AssetTag { + fn new_custom(iface_name: &str, data: &str) -> Self { + let rand = thread_rng().next_u64(); + let timestamp = SystemTime::now().elapsed().expect("system time error"); + let mut hasher = Sha256::default(); + hasher.input_with_len::(iface_name.as_bytes()); + hasher.input_with_len::(data.as_bytes()); + hasher.input_raw(×tamp.as_nanos().to_le_bytes()); + hasher.input_raw(&rand.to_le_bytes()); + AssetTag::from(hasher.finish()) + } + } +} From a17da6babe13abfbee8a31497b958091e284a26d Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 16 Nov 2023 13:38:54 +0100 Subject: [PATCH 02/12] persistance: support asset tags --- src/containers/consignment.rs | 10 +++++++--- src/interface/builder.rs | 37 +++++++++++++++++++++++------------ src/persistence/hoard.rs | 20 +++++++++++++++++-- src/persistence/inventory.rs | 4 +++- src/persistence/stash.rs | 10 ++++++++-- src/stl/stl.rs | 2 +- 6 files changed, 61 insertions(+), 22 deletions(-) diff --git a/src/containers/consignment.rs b/src/containers/consignment.rs index ea047bfb..809eb520 100644 --- a/src/containers/consignment.rs +++ b/src/containers/consignment.rs @@ -88,7 +88,7 @@ pub struct Consignment { pub supplements: TinyOrdSet, /// Confidential asset tags. - pub asset_tags: SmallOrdMap, + pub asset_tags: TinyOrdMap, /// Genesis data. pub genesis: Genesis, @@ -119,7 +119,11 @@ impl Consignment { /// # Panics /// /// If the provided schema is not the one which is used by genesis. - pub fn new(schema: SubSchema, genesis: Genesis) -> Self { + pub fn new( + schema: SubSchema, + genesis: Genesis, + asset_tags: TinyOrdMap, + ) -> Self { assert_eq!(schema.schema_id(), genesis.schema_id); Consignment { validation_status: None, @@ -128,7 +132,7 @@ impl Consignment { schema, ifaces: none!(), supplements: none!(), - asset_tags: none!(), + asset_tags, genesis, terminals: none!(), bundles: none!(), diff --git a/src/interface/builder.rs b/src/interface/builder.rs index d91687b7..2262b7de 100644 --- a/src/interface/builder.rs +++ b/src/interface/builder.rs @@ -19,7 +19,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use amplify::confinement::{Confined, TinyOrdMap, U16, U8}; use amplify::{confinement, Wrapper}; @@ -141,9 +141,13 @@ impl ContractBuilder { } #[inline] - pub fn add_asset_tag(mut self, assignment_type: AssignmentType, asset_tag: AssetTag) -> Self { - self.builder = self.builder.add_asset_tag(assignment_type, asset_tag); - self + pub fn add_asset_tag( + mut self, + assignment_type: AssignmentType, + asset_tag: AssetTag, + ) -> Result { + self.builder = self.builder.add_asset_tag(assignment_type, asset_tag)?; + Ok(self) } pub fn assignments_type(&self, name: &FieldName) -> Option { @@ -208,7 +212,7 @@ impl ContractBuilder { } pub fn issue_contract(self) -> Result { - let (schema, iface_pair, global, assignments) = self.builder.complete(); + let (schema, iface_pair, global, assignments, asset_tags) = self.builder.complete(); let genesis = Genesis { ffv: none!(), @@ -223,7 +227,7 @@ impl ContractBuilder { // TODO: Validate against schema - let mut contract = Contract::new(schema, genesis); + let mut contract = Contract::new(schema, genesis, asset_tags); contract.ifaces = tiny_bmap! { iface_pair.iface_id() => iface_pair }; Ok(contract) @@ -393,7 +397,7 @@ impl TransitionBuilder { } pub fn complete_transition(self, contract_id: ContractId) -> Result { - let (_, _, global, assignments) = self.builder.complete(); + let (_, _, global, assignments, _) = self.builder.complete(); let transition = Transition { ffv: none!(), @@ -418,7 +422,7 @@ struct OperationBuilder { schema: SubSchema, iface: Iface, iimpl: IfaceImpl, - asset_tags: BTreeMap, + asset_tags: TinyOrdMap, global: GlobalState, // rights: TinyOrdMap>, 1, U8>>, @@ -455,9 +459,13 @@ impl OperationBuilder { } #[inline] - pub fn add_asset_tag(mut self, assignment_type: AssignmentType, asset_tag: AssetTag) -> Self { - self.asset_tags.insert(assignment_type, asset_tag); - self + pub fn add_asset_tag( + mut self, + assignment_type: AssignmentType, + asset_tag: AssetTag, + ) -> Result { + self.asset_tags.insert(assignment_type, asset_tag)?; + Ok(self) } pub fn add_global_state( @@ -555,7 +563,10 @@ impl OperationBuilder { Ok(self) } - fn complete(self) -> (SubSchema, IfacePair, GlobalState, Assignments) { + fn complete( + self, + ) -> (SubSchema, IfacePair, GlobalState, Assignments, TinyOrdMap) + { let owned_state = self.fungible.into_iter().map(|(id, vec)| { let vec = vec.into_iter().map(|(seal, value)| match seal { BuilderSeal::Revealed(seal) => Assign::Revealed { seal, state: value }, @@ -585,6 +596,6 @@ impl OperationBuilder { let iface_pair = IfacePair::with(self.iface, self.iimpl); - (self.schema, iface_pair, self.global, assignments) + (self.schema, iface_pair, self.global, assignments, self.asset_tags) } } diff --git a/src/persistence/hoard.rs b/src/persistence/hoard.rs index 378cb188..481477b7 100644 --- a/src/persistence/hoard.rs +++ b/src/persistence/hoard.rs @@ -27,8 +27,8 @@ use amplify::confinement::{Confined, LargeOrdMap, SmallOrdMap, TinyOrdMap, TinyO use bp::dbc::anchor::MergeError; use commit_verify::mpc; use rgb::{ - Anchor, AnchorId, AnchoredBundle, BundleId, ContractId, Extension, Genesis, OpId, Operation, - SchemaId, TransitionBundle, + Anchor, AnchorId, AnchoredBundle, AssetTag, AssignmentType, BundleId, ContractId, Extension, + Genesis, OpId, Operation, SchemaId, TransitionBundle, }; use strict_encoding::TypeName; @@ -67,6 +67,7 @@ pub struct Hoard { pub(super) ifaces: TinyOrdMap, pub(super) geneses: TinyOrdMap, pub(super) suppl: TinyOrdMap>, + pub(super) asset_tags: TinyOrdMap>, pub(super) bundles: LargeOrdMap, pub(super) extensions: LargeOrdMap, pub(super) anchors: LargeOrdMap>, @@ -84,6 +85,7 @@ impl Hoard { }, geneses: none!(), suppl: none!(), + asset_tags: none!(), bundles: none!(), extensions: none!(), anchors: none!(), @@ -182,6 +184,10 @@ impl Hoard { self.import_sigs_internal(content_id, sigs).ok(); } + // Update asset tags + self.asset_tags + .insert(contract_id, consignment.asset_tags.clone())?; + Ok(()) } @@ -308,4 +314,14 @@ impl Stash for Hoard { .get(&anchor_id) .ok_or(StashInconsistency::AnchorAbsent(anchor_id).into()) } + + fn contract_asset_tags( + &self, + contract_id: ContractId, + ) -> Result<&TinyOrdMap, StashError> { + self.asset_tags + .get(&contract_id) + .ok_or(StashInconsistency::ContractAbsent(contract_id)) + .map_err(StashError::from) + } } diff --git a/src/persistence/inventory.rs b/src/persistence/inventory.rs index 5ccab66c..83995e17 100644 --- a/src/persistence/inventory.rs +++ b/src/persistence/inventory.rs @@ -589,7 +589,9 @@ pub trait Inventory: Deref { let genesis = self.genesis(contract_id)?; let schema_ifaces = self.schema(genesis.schema_id)?; - let mut consignment = Consignment::new(schema_ifaces.schema.clone(), genesis.clone()); + let asset_tags = self.contract_asset_tags(contract_id)?; + let mut consignment = + Consignment::new(schema_ifaces.schema.clone(), genesis.clone(), asset_tags.clone()); for (iface_id, iimpl) in &schema_ifaces.iimpls { let iface = self.iface_by_id(*iface_id)?; consignment diff --git a/src/persistence/stash.rs b/src/persistence/stash.rs index a91daecb..2224e3f5 100644 --- a/src/persistence/stash.rs +++ b/src/persistence/stash.rs @@ -24,10 +24,11 @@ use std::collections::{BTreeMap, BTreeSet}; use std::error::Error; -use amplify::confinement::TinyOrdSet; +use amplify::confinement::{TinyOrdMap, TinyOrdSet}; use commit_verify::mpc; use rgb::{ - Anchor, AnchorId, BundleId, ContractId, Extension, Genesis, OpId, SchemaId, TransitionBundle, + Anchor, AnchorId, AssetTag, AssignmentType, BundleId, ContractId, Extension, Genesis, OpId, + SchemaId, TransitionBundle, }; use strict_encoding::TypeName; @@ -113,6 +114,11 @@ pub trait Stash { fn contract_suppl(&self, contract_id: ContractId) -> Option<&TinyOrdSet>; + fn contract_asset_tags( + &self, + contract_id: ContractId, + ) -> Result<&TinyOrdMap, StashError>; + fn genesis(&self, contract_id: ContractId) -> Result<&Genesis, StashError>; fn bundle_ids(&self) -> Result, Self::Error>; diff --git a/src/stl/stl.rs b/src/stl/stl.rs index 8fc9f852..b52cccd2 100644 --- a/src/stl/stl.rs +++ b/src/stl/stl.rs @@ -42,7 +42,7 @@ pub const LIB_ID_RGB_CONTRACT: &str = /// Strict types id for the library representing of RGB StdLib data types. pub const LIB_ID_RGB_STD: &str = - "urn:ubideco:stl:BweL96SmzWcFTCe1w9hcjaVDqStsHRA54TuTkT9fTvNh#violet-balance-village"; + "urn:ubideco:stl:6TnTQi6dGTH8JqSE2QJq1bF5uPXx67Kk8Vsc3sQE6ft#lemon-pilgrim-lecture"; fn _rgb_std_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB_STD), tiny_bset! { From 624bedd147b2ae98b9dd736969a4d190c1b833a6 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 16 Nov 2023 13:40:43 +0100 Subject: [PATCH 03/12] stl: add Stock and Hoard to the library --- src/stl/stl.rs | 4 +- stl/RGBStd@0.1.0.sta | 708 ++++++++++++++++++++++++------------------- stl/RGBStd@0.1.0.stl | Bin 16067 -> 19782 bytes stl/RGBStd@0.1.0.sty | 53 +++- 4 files changed, 444 insertions(+), 321 deletions(-) diff --git a/src/stl/stl.rs b/src/stl/stl.rs index b52cccd2..8fbc5200 100644 --- a/src/stl/stl.rs +++ b/src/stl/stl.rs @@ -32,6 +32,7 @@ use super::{ RicardianContract, Timestamp, LIB_NAME_RGB_CONTRACT, }; use crate::containers::{Contract, Transfer}; +use crate::persistence::Stock; use crate::stl::ProofOfReserves; use crate::LIB_NAME_RGB_STD; @@ -42,7 +43,7 @@ pub const LIB_ID_RGB_CONTRACT: &str = /// Strict types id for the library representing of RGB StdLib data types. pub const LIB_ID_RGB_STD: &str = - "urn:ubideco:stl:6TnTQi6dGTH8JqSE2QJq1bF5uPXx67Kk8Vsc3sQE6ft#lemon-pilgrim-lecture"; + "urn:ubideco:stl:5BWvsBkteVgD3yiGy8gHHuzhHFLQTz4mZU7kJob3JkHW#lazarus-ranger-neutral"; fn _rgb_std_stl() -> Result { LibBuilder::new(libname!(LIB_NAME_RGB_STD), tiny_bset! { @@ -56,6 +57,7 @@ fn _rgb_std_stl() -> Result { }) .transpile::() .transpile::() + .transpile::() .compile() } diff --git a/stl/RGBStd@0.1.0.sta b/stl/RGBStd@0.1.0.sta index 3504e144..bb2ca86e 100644 --- a/stl/RGBStd@0.1.0.sta +++ b/stl/RGBStd@0.1.0.sta @@ -1,5 +1,5 @@ -----BEGIN STRICT TYPE LIB----- -Id: urn:ubideco:stl:BweL96SmzWcFTCe1w9hcjaVDqStsHRA54TuTkT9fTvNh +Id: urn:ubideco:stl:5BWvsBkteVgD3yiGy8gHHuzhHFLQTz4mZU7kJob3JkHW Name: RGBStd Dependencies: urn:ubideco:stl:5dLgsvZEbhQekV5j7ghFCTyu3JPZ1B99SqhqPEA4Fdc, @@ -17,7 +17,7 @@ LDm8cNL96PlDrg39nPTmgu3HZspwA1N0ZLmzB6Bap1ZJhkNCbroWCz+PjGj56E/9 zS2FQAp57Q9gBUFsdVZNwit7ask8TqWm+/r8Wolw6m4OOVen+A89R6ZzlSgGrKoG QlBDb3Jl9WwTYiP2OadKCZPcR0bJ+YqruINYXbXZFj8YfsQoGgoHQml0Y29pbgcF QWx1Vk0CAG3voSbhvHXh/0hL+4XBNNEMMtyMHkDgaUsc1qfr3NxhB0xpYlNpdGWn -MFUCLflcyPCJo0WiP5beUSnAE7cO8SfYIZBBlftTCgVMaWJJZAZCUENvcmULAAF8 +MFUCLflcyPCJo0WiP5beUSnAE7cO8SfYIZBBlftTCgVMaWJJZAZCUENvcmUNAAF8 B10AQEsWlZgbF8NhLca46q4Nf3BZYpIWdVrlGZMREVRhcHJldE5vZGVQYXJ0bmVy DFBskkmcWPMvLuwsVLjXFmu8mBTsPpkCRT1xLrphCeENQmxpbmRTZWFsVHhpZA+2 H5g/Gu2rjnvK5hyt61m+s5sC5IXzN5lwiJbZEwgMC1RhcHJldFByb29mE8RTUmYn @@ -28,323 +28,401 @@ VpNZ3ixLTcNz9Eo2jG7LZ2jFXeMnqjPfO7Xw3BFBbmNob3JNZXJrbGVQcm9vZmgZ b7i9lbu7y/XhxSWJRdIRdtoe1NyMxTElZQ5CbGluZFNlYWxUeFB0crHlODkUCji+ 8G8az74cYKVv4eH0fXgIKHm/0frTECHdBVR4UHRy0lIwfH1xkDX3MH7oKCXsG4Er oYfdnZhJi0qNFvpu1UMLQ2xvc2VNZXRob2TUlmYB3jzummNgw+4NYLL9m9n4vO9o -G2GrUIEodbTLhAVQcm9vZgdCaXRjb2luFAABGW2FKcj22kRNFU6NnIy9ng+NiQJa -O7CRIcY9UrAehwVTZXFObwoZkNdT8gqVZh8TO/UjSiWP/kvI3QknpBvkBL2Ookaq -BFR4SW4h4z5Dxapc8iknU6M4wWftO2OcTdnOvamPNGkXuslDdQRWb3V0Jav1uRIU -F7qjOdRfexV1p3FL4Xp1GF3QMTV61Mkt6YYLU2NyaXB0Qnl0ZXMxu67ohIl3xbAH -MXIxzZL2MLYpLc2Jf9y63sW6xOl/2QtUYXBOb2RlSGFzaDXaHRU5IG673dykwz2H -Merym6fadN89yIIgHE4WtbkcCExvY2tUaW1lOHUEsUt8DkZtR54XR30kdCR/+ANk -7D1Lqvc6IPdBdGsJU2lnU2NyaXB0X6zZbeU/TsUU2bGNZ4DaCqvrLSYL/Tcto8B6 -pF05n00KTGVhZlNjcmlwdJA7ZHB5hI8bJlMq5M7GpolEoFNYuPCByyazu/gtnjvz -BVR4T3V0l/XXBkKuKjOSJTuoTh3OxJPjvz7TcbGHc4Y1TsyIgmsEU2F0c51Vk6y6 -+c3gmzhG+IMJcdM4yS9udbx84yBFnqs4GaYeB1dpdG5lc3OjgkLzy9fR0KES2o3h -YC9W1PhvDsTEdsXAaFlMSwRlVgRUeGlkqHzGeWRn5VfXROXJYjrgDMuV2RoNLwPf -dMB1Ek++x54FVHhWZXKphYR3U55o+7C/7seaIcRXQ8FU+Pq9P5jg05E957c3eApJ -bnRlcm5hbFBrtjMJqRi/tpINbshYpCSHI0ZaaT9yQwe//x3XOnOBTskHTGVhZlZl -cr78HvxmpRn9ZFJqOhOHQOfxEC0Lvv86wUZO8/dAdnRcDFNjcmlwdFB1YmtlecRy -a4+PKNF6wZfIHY4NoZtiRJzm0GRd/sfpeDy1h/GjB0J5dGVTdHLFeyGav/c5bnJG -gK1FVi987KFsbj8m3dz//H5sA+bcRAJUeOhqQM1cJfm94oT/aaURMqdBKyFVvQ5W -EsG/44SVYMUGCE91dHBvaW50/KKnKr6R/s2CqLGYkGMiwk52qqo18iTRcwVjoBgu -iFkHWE9ubHlQawxDb21taXRWZXJpZnkCAC/uzx5E0qEpuYoUOEdLOXGVKygcogGS -1RMm+LI2YF5nC01lcmtsZVByb29mVY03B/hFhlOA7sxBVSTopJlgUdOUgkPxlPfx -kVcj6eYKTWVya2xlTm9kZQNSR0JDAAEKBzIpSOEAza9OwAD0KkRKUEetGVoIsSuk -Z1qhD07BIEFzc2lnblJldmVhbGVkVmFsdWVCbGluZFNlYWxUeGlkA3uTtT0ahS4H -WQEld822QfAIrU7nAFhhg7XASr3gAlwIQnVuZGxlSWQP+aNjbGPkkqopcG754Nrt -T7ZHuV77qjvpdbNGEwz2fCFBc3NpZ25SZXZlYWxlZEF0dGFjaEJsaW5kU2VhbFR4 -aWQQQYJyNRcc2YKVkqaZF/4DcT+tb+LTIU6Iwf4co6cKnQxTY2hlbWFTY2hlbWEa -BlQW1wHBizvL/lyc0UYqVhVEB88o3IpNZg1RAyq0MQZBbmNob3IedhfxJ33bPrvh -ag9yEbdt7VXfb0MNVRFfA3gnpUJXJwlOb2lzZUR1bWIfo1Y8MaKmik0VkfdJfaIb -eEibMPMjwOzzM7Q6rI/UaAlFeHRlbnNpb24gtPgFLprooY1H12+XMUqIqKiMFf4F -Y7lsUqmPNKhU0xBUcmFuc2l0aW9uQnVuZGxlIPJQViKPZs78kfrqZ7xfxvBpXD1H -eXNZP6zRN8pySGUMUmV2ZWFsZWREYXRhIiginEN2PcizQprhAmty41DZdy9s4y6M -KYoFX2dX8iUbU2VhbERlZmluaXRpb25CbGluZFNlYWxUeGlkIwG4lMQ9NH4CH0Sp -QuJTMC2TDDzNGTMLtJABONZedqcKVHJhbnNpdGlvbiRXUthlgPCcymojN1QSRCBg -fKMsf+7M8RQQAG8KCSqMDEFsdExheWVyMVNldCfofjYNodsf82aD2qTHrPHpIUun -BMohtnHh6+qB1SyoGlR5cGVkQXNzaWduc0JsaW5kU2VhbFR4UHRyKWYu7v5geWxw -u1p8bY9yjOjqS8tT83pMy5WTaAianu4dQXNzaWduVm9pZFN0YXRlQmxpbmRTZWFs -VHhQdHIusqX+F8AwRDCY4FybG1ps1h7tdg1h3suAJtNk8xbhuwlWb2lkU3RhdGU0 -Ug+uE5YaXr0p/BEjP4VO8hA4BH/UBL7foUbZFqUyaA5UcmFuc2l0aW9uVHlwZTbB -NKH6oIETp90wgDxesPLC/doUsNWj1pPDbp4PMqp5C09jY3VycmVuY2VzOMoU4IUi -x+M5gkaUssOZWHVTpujKDA7h6JWJuol3G/MLU3RhdGVTY2hlbWE9v1bS7o20MOov -a/O7am6xwblRSrw5Zad1j4BYB5yPugZTY2hlbWE+3J1wNEPkBuDqB11Dgy/Nr1/L -VPO0gJ1pu+1/YKRBox9Bc3NpZ25SZXZlYWxlZERhdGFCbGluZFNlYWxUeGlkQjBh -hYjI1KsUJkDH/ckXLM3Q/xD/DZOAAER8iJrdzHkJTWVkaWFUeXBlQ5SjO9oXhBY/ -31qnJYuTX0VGAT/hgnvsLBhfPBZZTxoZVHlwZWRBc3NpZ25zQmxpbmRTZWFsVHhp -ZEUqpV991gFKi6FtxGYytg/xtWzcOlMuz71rk9VnGZ6JDFJlc2VydmVkQnl0ZUY0 -faUe7WgMXvtvL6bEb+nZw1LviY4pspRGpo3GoF6mDEdsb2JhbFZhbHVlc0bt5sMI -HP245lekKlzgxgiEf/wfvl52uXF0qcr1iVnOC1ZhbGVuY3lUeXBlSL0abhf7hjsW -fH4lXjVn24JD7ypeuuuixQrNCa6eURoSUGVkZXJzZW5Db21taXRtZW50TA5mElZh -WYYrAaikUtKPFKCFum5wG6hAPcxiD+J2regcU2VhbERlZmluaXRpb25CbGluZFNl -YWxUeFB0clmiUc2N5suJJETtz3Vbsaql3Bw/2sE0zWwlNtX2E4rCIEFzc2lnblJl -dmVhbGVkRGF0YUJsaW5kU2VhbFR4UHRyWeBhxC5j1wObpR3XfiuhoWyjxYVIZJcw -oRFZkqAhzFQOQW5jaG9yZWRCdW5kbGVkdR5CqRWhPEMRgtX/htUc00Rwo5DhSuyg -UMw6U29I3g1FeHRlbnNpb25UeXBlZrSXhI/EeTlg3zSS63YSkLMOrBDRPTdULVJk -EQiUUHEPRXh0ZW5zaW9uU2NoZW1haFM0IAzcU8ZNduK2LqpmL9o62ZG/BqspDrzQ -9noDSswOUmV2ZWFsZWRBdHRhY2htRMb0L2oFOkMVD6bmwizE6HrlLJT4BtQMpdyH -9nWNoBBSZXZlYWxlZEZ1bmdpYmxlcA1lELKCgW8ftz3lEmOTA1cIpVJhrQLg1L5a -xc+n6gkNQ29uY2VhbGVkRGF0YYP6TOCuW46PGuAuFO5uYmKV9W4Am0INDeMH9TJu -Auw6B0dlbmVzaXOEcQ2TLE70w7cIS2mLsIdwKX45ZCR/RZHY2oNt0X26OAhBdHRh -Y2hJZIW4+Cu79KSmDbO/P0W4D5RueIPDrVJtk/RvowGobkfaDkJsaW5kaW5nRmFj -dG9yh/7iZViIbOgcvoaEs3ljJxNlg8W2aAFeixQWNrFEh7kOQXNzaWdubWVudFR5 -cGWTELyAsTRaiy/DWFLuD01o0B23+jXLm2SSq1YJmvSalwVPcG91dJRS09sDq6uo -UZ+n9j7QFOvUTX0xP/z+APpdJHpUBJAQCFNjaGVtYUlklcjmeh51Yl/UllVCapHr -fkKQoW9amPG+UPe2iiQS98UET3BJZJjmCNUpvD8wzk9MqgCsLOdH/s86QuxysgqC -wL4CEwKJHEFzc2lnblZvaWRTdGF0ZUJsaW5kU2VhbFR4aWSZlKWKvR08VEUvKbrl -Sc0Al+cglJS/Z0dfSGrKLPhWFyFBc3NpZ25SZXZlYWxlZFZhbHVlQmxpbmRTZWFs -VHhQdHKfCCxJOsgCorrF3dwLInwgr5TUaMRIzxpaIeC9wvU6MgpDb250cmFjdElk -ojPJ6ChOzHWY/AY55zTQywKMSyzchJE+mdmqRj4k1lILR2xvYmFsU3RhdGWi+uqe -cFyckczb3Ubtj3DljvUUgz7IPlxktWpbw1OtsAlBbHVTY3JpcHSlptR7nqJ4GRdu -bdPyKWaTdsD+DslVtMTrjZBSuBQsnwpCdW5kbGVJdGVtpowwkUdLyjrCYozHlUSN -LN2qxRSfBTOVq/Y8VWEHFyYNRnVuZ2libGVTdGF0ZahYa/iRZdrCIxBtvYXEhsk3 -5rBm6wxmQf8WL2iod6WYClJhbmdlUHJvb2arQv8KxJBqRRD4VF8GJCpNCYyQVViz -09s3LHCMWeBMQw1HZW5lc2lzU2NoZW1htRWURiX16DrteJt7itR6DTKoivrLIO0E -Sp8iRPogQ70YQXNzaWdubWVudHNCbGluZFNlYWxUeGlkvcdveMo95d9+PKqU9FGU -nC0VNYeAXXvK6KRai0nxAX8QVHJhbnNpdGlvblNjaGVtYcJRrWXpdQ2smhJZzMCF -JFzV97FvUthyNkYs8XWMn05dEUNvbmNlYWxlZEZ1bmdpYmxlxhhje2dNDLS8qcBD -XX8yYoOYeHN0J0PRN+VE+7oS0EwGU2NyaXB0x5im2GM2eEQe2lFuLD6Lvw6osEqA -wbcduely5j9x5iQRR2xvYmFsU3RhdGVTY2hlbWHIa4J7C1p9xpEEJHLlIieP0M/F -GldooEs/qjFAGzx+IwlBbHRMYXllcjHJQkIi7QK+R2n9TyMds0VOpyi8Q6gRDtsz -y48vCp5IRgVJbnB1dMmPmqnDBksa/f1nG5e8Kvz+TGiPH3PILetxb+dpmyKvCEFz -c2V0VGFn1e6SDkmIs2nxalPRB0r7tSP4x1JxHvs2PVZZBeHkFcsPR2xvYmFsU3Rh -dGVUeXBl2ptRE1gWVnaQh/uZ5VaUcjaA1zkMBqHMJJgsBWT3zNUDRmZ23YVmAG9h -ZBEU7o7x16r4CbMaJLCqJ6mbsjDoqs8pR00JVmFsZW5jaWVz38+pkfWH5U1EtwEV -XAAp/JVrm/HNqUGOYziWqsAqg+gIUmVkZWVtZWTi8tuzmTSI9GNgcXARevZlWteS -9+UM2b3VyT4M4euxIBlBc3NpZ25tZW50c0JsaW5kU2VhbFR4UHRy+XhNoLZD3jl8 -HtlXjTOMN9Ecy+JKweinmkzk1fwYJ6sGSW5wdXRz+fSsCGauQXdm1P0MEX0EsdlU -/Q5nXhI7YgTrcU/p5UYMRnVuZ2libGVUeXBl+8wXdhU5F6Y0puG+4B7dK9xiv6+J -WuV1OXL/KkGX2IAiQXNzaWduUmV2ZWFsZWRBdHRhY2hCbGluZFNlYWxUeFB0cvw0 -Rd1fzw0L/6wVpHq6BSLgW4srGzBlUoeDj3st6449D0NvbmNlYWxlZEF0dGFjaANT -dGQDABRNrsgy5sQ3knslXrmbQoqH2Q1QqMzQTXIRtbe8X2bOBUFzY2lpYYYi0Xuu -8GYC3+d1yYDgs2tuuugJDYB191E77EuT9k0EQm9vbHKOpoqX3nQg9ipZabBLhyYE -v0XW3ziVnH4m56ckkOStDkFscGhhTnVtTG9kYXNoC1N0cmljdFR5cGVzEgANFCNl -8qSvxhtTbdRnVXJmK7Byo5APyYsAZorYMYDisAhUeXBlTmFtZSRj2r98SvHqkSDv -v4DCx4mhV5LU8fujLFiYI7EPaSH6CVByaW1pdGl2ZSjVuVhQ3C1VjNAoJdORbt1u -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 +muECa3LjUNl3L2zjLowpigVfZ1fyJRtTZWFsRGVmaW5pdGlvbkJsaW5kU2VhbFR4 +aWQjAbiUxD00fgIfRKlC4lMwLZMMPM0ZMwu0kAE41l52pwpUcmFuc2l0aW9uJFdS +2GWA8JzKaiM3VBJEIGB8oyx/7szxFBAAbwoJKowMQWx0TGF5ZXIxU2V0J+h+Ng2h +2x/zZoPapMes8ekhS6cEyiG2ceHr6oHVLKgaVHlwZWRBc3NpZ25zQmxpbmRTZWFs +VHhQdHIpZi7u/mB5bHC7Wnxtj3KM6OpLy1PzekzLlZNoCJqe7h1Bc3NpZ25Wb2lk +U3RhdGVCbGluZFNlYWxUeFB0ci6ypf4XwDBEMJjgXJsbWmzWHu12DWHey4Am02Tz +FuG7CVZvaWRTdGF0ZTRSD64TlhpevSn8ESM/hU7yEDgEf9QEvt+hRtkWpTJoDlRy +YW5zaXRpb25UeXBlNsE0ofqggROn3TCAPF6w8sL92hSw1aPWk8Nung8yqnkLT2Nj +dXJyZW5jZXM4yhTghSLH4zmCRpSyw5lYdVOm6MoMDuHolYm6iXcb8wtTdGF0ZVNj +aGVtYToO+q7WKbiEC3hH3UY0KLhDBCelBMd2qpK/+UDCs+92HU91dHB1dEFzc2ln +bm1lbnRSZXZlYWxlZFZhbHVlPb9W0u6NtDDqL2vzu2puscG5UUq8OWWndY+AWAec +j7oGU2NoZW1hPtydcDRD5Abg6gddQ4Mvza9fy1TztICdabvtf2CkQaMfQXNzaWdu +UmV2ZWFsZWREYXRhQmxpbmRTZWFsVHhpZEIwYYWIyNSrFCZAx/3JFyzN0P8Q/w2T +gABEfIia3cx5CU1lZGlhVHlwZUOUozvaF4QWP99apyWLk19FRgE/4YJ77CwYXzwW +WU8aGVR5cGVkQXNzaWduc0JsaW5kU2VhbFR4aWRFKqVffdYBSouhbcRmMrYP8bVs +3DpTLs+9a5PVZxmeiQxSZXNlcnZlZEJ5dGVGNH2lHu1oDF77by+mxG/p2cNS74mO +KbKURqaNxqBepgxHbG9iYWxWYWx1ZXNG7ebDCBz9uOZXpCpc4MYIhH/8H75edrlx +dKnK9YlZzgtWYWxlbmN5VHlwZUi9Gm4X+4Y7Fnx+JV41Z9uCQ+8qXrrrosUKzQmu +nlEaElBlZGVyc2VuQ29tbWl0bWVudEwOZhJWYVmGKwGopFLSjxSghbpucBuoQD3M +Yg/idq3oHFNlYWxEZWZpbml0aW9uQmxpbmRTZWFsVHhQdHJRcFb/JTa0M+NTgQaX +gPTuDQo5vskjWNzPydduxPGH2QZPdXRwdXRZolHNjebLiSRE7c91W7GqpdwcP9rB +NM1sJTbV9hOKwiBBc3NpZ25SZXZlYWxlZERhdGFCbGluZFNlYWxUeFB0clngYcQu +Y9cDm6Ud134roaFso8WFSGSXMKERWZKgIcxUDkFuY2hvcmVkQnVuZGxlZHUeQqkV +oTxDEYLV/4bVHNNEcKOQ4UrsoFDMOlNvSN4NRXh0ZW5zaW9uVHlwZWa0l4SPxHk5 +YN80kut2EpCzDqwQ0T03VC1SZBEIlFBxD0V4dGVuc2lvblNjaGVtYWhTNCAM3FPG +TXbiti6qZi/aOtmRvwarKQ680PZ6A0rMDlJldmVhbGVkQXR0YWNobUTG9C9qBTpD +FQ+m5sIsxOh65SyU+AbUDKXch/Z1jaAQUmV2ZWFsZWRGdW5naWJsZXANZRCygoFv +H7c95RJjkwNXCKVSYa0C4NS+WsXPp+oJDUNvbmNlYWxlZERhdGF52xr8CB6hdXgQ +w8hr7HE9twtbmdr9g/hGMltC0aMcWR5PdXRwdXRBc3NpZ25tZW50UmV2ZWFsZWRB +dHRhY2h69XkNjG4gtH3wH/XNhWn1y7zxEMGvZWy1kKKWtKv/AQlHbG9iYWxPcmSD ++kzgrluOjxrgLhTubmJilfVuAJtCDQ3jB/UybgLsOgdHZW5lc2lzhHENkyxO9MO3 +CEtpi7CHcCl+OWQkf0WR2NqDbdF9ujgIQXR0YWNoSWSFuPgru/Skpg2zvz9FuA+U +bniDw61SbZP0b6MBqG5H2g5CbGluZGluZ0ZhY3Rvcof+4mVYiGzoHL6GhLN5YycT +ZYPFtmgBXosUFjaxRIe5DkFzc2lnbm1lbnRUeXBlkxC8gLE0Wosvw1hS7g9NaNAd +t/o1y5tkkqtWCZr0mpcFT3BvdXSUUtPbA6urqFGfp/Y+0BTr1E19MT/8/gD6XSR6 +VASQEAhTY2hlbWFJZJXI5noedWJf1JZVQmqR635CkKFvWpjxvlD3tookEvfFBE9w +SWSY5gjVKbw/MM5PTKoArCznR/7POkLscrIKgsC+AhMCiRxBc3NpZ25Wb2lkU3Rh +dGVCbGluZFNlYWxUeGlkmZSlir0dPFRFLym65UnNAJfnIJSUv2dHX0hqyiz4Vhch +QXNzaWduUmV2ZWFsZWRWYWx1ZUJsaW5kU2VhbFR4UHRynwgsSTrIAqK6xd3cCyJ8 +IK+U1GjESM8aWiHgvcL1OjIKQ29udHJhY3RJZKIzyegoTsx1mPwGOec00MsCjEss +3ISRPpnZqkY+JNZSC0dsb2JhbFN0YXRlovrqnnBcnJHM291G7Y9w5Y71FIM+yD5c +ZLVqW8NTrbAJQWx1U2NyaXB0pabUe56ieBkXbm3T8ilmk3bA/g7JVbTE642QUrgU +LJ8KQnVuZGxlSXRlbaaMMJFHS8o6wmKMx5VEjSzdqsUUnwUzlav2PFVhBxcmDUZ1 +bmdpYmxlU3RhdGWoWGv4kWXawiMQbb2FxIbJN+awZusMZkH/Fi9oqHelmApSYW5n +ZVByb29mq0L/CsSQakUQ+FRfBiQqTQmMkFVYs9PbNyxwjFngTEMNR2VuZXNpc1Nj +aGVtYbGE2FqNX4O/S8m9j3uvw5TT6slgbWmmash4pOYVT7MiEUFuY2hvck1lcmts +ZUJsb2NrtRWURiX16DrteJt7itR6DTKoivrLIO0ESp8iRPogQ70YQXNzaWdubWVu +dHNCbGluZFNlYWxUeGlkvcdveMo95d9+PKqU9FGUnC0VNYeAXXvK6KRai0nxAX8Q +VHJhbnNpdGlvblNjaGVtYcIe7NwA077i648Cm3I6+7EQwDaX6c8DaBmUFaYEB2nK +CldpdG5lc3NPcmTCUa1l6XUNrJoSWczAhSRc1fexb1LYcjZGLPF1jJ9OXRFDb25j +ZWFsZWRGdW5naWJsZcSgCp7hCQITdyIBFVk7g8NT4mD4gRDkszbK42hGQScbCVdp +dG5lc3NJZMYYY3tnTQy0vKnAQ11/MmKDmHhzdCdD0TflRPu6EtBMBlNjcmlwdMeY +pthjNnhEHtpRbiw+i78OqLBKgMG3HbnpcuY/ceYkEUdsb2JhbFN0YXRlU2NoZW1h +yGuCewtafcaRBCRy5SInj9DPxRpXaKBLP6oxQBs8fiMJQWx0TGF5ZXIxyUJCIu0C +vkdp/U8jHbNFTqcovEOoEQ7bM8uPLwqeSEYFSW5wdXTJj5qpwwZLGv39ZxuXvCr8 +/kxojx9zyC3rcW/naZsirwhBc3NldFRhZ9Dt3q+bZlQ2vSmW24KeCQCmg7JWb0Ky +ZfKw+0WI+q8RGU91dHB1dEFzc2lnbm1lbnRWb2lkU3RhdGXV7pIOSYizafFqU9EH +Svu1I/jHUnEe+zY9VlkF4eQVyw9HbG9iYWxTdGF0ZVR5cGXYY93FeLsPNcGN8j2e +uhtJkH0Sl+eDrKaLkdAx6PyXYxxPdXRwdXRBc3NpZ25tZW50UmV2ZWFsZWREYXRh +2ptRE1gWVnaQh/uZ5VaUcjaA1zkMBqHMJJgsBWT3zNUDRmZ23YVmAG9hZBEU7o7x +16r4CbMaJLCqJ6mbsjDoqs8pR00JVmFsZW5jaWVz38+pkfWH5U1EtwEVXAAp/JVr +m/HNqUGOYziWqsAqg+gIUmVkZWVtZWTi8tuzmTSI9GNgcXARevZlWteS9+UM2b3V +yT4M4euxIBlBc3NpZ25tZW50c0JsaW5kU2VhbFR4UHRy6ta3unkK4FGpfiwZ+Pwh +nMW+Kp3v3/3VY6uveGsbggsNV2l0bmVzc0FuY2hvcvl4TaC2Q945fB7ZV40zjDfR +HMviSsHop5pM5NX8GCerBklucHV0c/n0rAhmrkF3ZtT9DBF9BLHZVP0OZ14SO2IE +63FP6eVGDEZ1bmdpYmxlVHlwZfvMF3YVORemNKbhvuAe3SvcYr+viVrldTly/ypB +l9iAIkFzc2lnblJldmVhbGVkQXR0YWNoQmxpbmRTZWFsVHhQdHL8NEXdX88NC/+s +FaR6ugUi4FuLKxswZVKHg497LeuOPQ9Db25jZWFsZWRBdHRhY2gDU3RkBAAUTa7I +MubEN5J7JV65m0KKh9kNUKjM0E1yEbW3vF9mzgVBc2NpaSLk4JbpvX1chvXh3113 +AWr+Occ82TSFUJRAiYyoo3leAlU1YYYi0Xuu8GYC3+d1yYDgs2tuuugJDYB191E7 +7EuT9k0EQm9vbHKOpoqX3nQg9ipZabBLhyYEv0XW3ziVnH4m56ckkOStDkFscGhh +TnVtTG9kYXNoC1N0cmljdFR5cGVzEgANFCNl8qSvxhtTbdRnVXJmK7Byo5APyYsA +ZorYMYDisAhUeXBlTmFtZSRj2r98SvHqkSDvv4DCx4mhV5LU8fujLFiYI7EPaSH6 +CVByaW1pdGl2ZSjVuVhQ3C1VjNAoJdORbt1u3PIXPcpAeYHXIbb/BAdbB1Zhcmlh +bnQuR1s+c8ngIm2OLCe6FLOqJb5tKPdHfiz9jE0oXhjsVgpUeXBlU3lzdGVtMZ9U +hpE3dGP87l9ke4b7zHs544yxJJE2nU/DI85FU5QQVmFyaWFudEluZm9TZW1JZD3+ +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 +ElZhWYYrAaikUtKPFKCFum5wG6hAPcxiD+J2regAAAAAAAAAAP///wAAAAAAB1N1 +cHBsSWQFAQAHAABAIAAIVGVybWluYWwGAgVzZWFscwAJAfKBrNHOWmzs0Ji0/qFl +qdcaZrrlQgkU5p+7HChkm2dIAAAAAAAAAAD//wAAAAAAAAJ0eAAEAgAEbm9uZQAA +AAEEc29tZQAFAQL1bBNiI/Y5p0oJk9xHRsn5iqu4g1hdtdkWPxh+xCgaCsV7IZq/ +9zluckaArUVWL3zsoWxuPybd3P/8fmwD5txEDFRlcm1pbmFsU2VhbAQDAA1jb25j +ZWFsZWRVdHhvAAUBAsIre2rJPE6lpvv6/FqJcOpuDjlXp/gPPUemc5UoBqyqaBnr +vNWzGKuXs5ilSzZl3dqnBm/o6STnA2CplLO9Bk4BEmJpdGNvaW5XaXRuZXNzVm91 +dAAFAQGt+AFDdAxLr3p1h4uBEAcGWYFcHSiVwBTAQUO6hp3r0wIRbGlxdWlkV2l0 +bmVzc1ZvdXQABQEBrfgBQ3QMS696dYeLgRAHBlmBXB0olcAUwEFDuoad69MLVGlj +a2VyU3VwcGwEAwAGYWJzZW50AAAAAQZnbG9iYWwABQICAS9pkUImnNjooCk5ArUF +SkqjNtilb4U8pf601YfNyuvV7pIOSYizafFqU9EHSvu1I/jHUnEe+zY9VlkF4eQV +ywJDNAOU2Bsw4lIokCYe82/5+Kg5UZH1C2leIyoes7dByNqamqop9exSJo8LUwsO +izT8xK3ks3urbgG9DUQNYWkgAgVvd25lZAAFAgIBL2mRQiac2OigKTkCtQVKSqM2 +2KVvhTyl/rTVh83K64f+4mVYiGzoHL6GhLN5YycTZYPFtmgBXosUFjaxRIe5AkM0 +A5TYGzDiUiiQJh7zb/n4qDlRkfULaV4jKh6zt0HI2pqaqin17FImjwtTCw6LNPzE +reSze6tuAb0NRA1haSAPVHJhbnNpdGlvbklmYWNlBggIb3B0aW9uYWwCe4SAPJ76 +4hElp3wsObxw0v3o+UOuDf2c9OaC7cdmynBhhiLRe67wZgLf53XJgOCza2666AkN +gHX3UTvsS5P2TQhtZXRhZGF0YQAEAgAEbm9uZQAAAAEEc29tZQAFAQJDNAOU2Bsw 4lIokCYe82/5+Kg5UZH1C2leIyoes7dByGsEoxSeppUAFmef7wK0qyT15reKZMjZ -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 -ZWxkb20PCGVwaXNvZGljHwdyZWd1bGFyPwhmcmVxdWVudH8NaGlnaEZyZXF1ZW5j -ef8FVmVyTm8DAQJ2MQAIVm91dFNlYWwGAwZtZXRob2QCwit7ask8TqWm+/r8Wolw -6m4OOVen+A89R6ZzlSgGrKrSUjB8fXGQNfcwfugoJewbgSuhh92dmEmLSo0W+m7V -QwR2b3V0AvVsE2Ij9jmnSgmT3EdGyfmKq7iDWF212RY/GH7EKBoKIeM+Q8WqXPIp -J1OjOMFn7TtjnE3Zzr2pjzRpF7rJQ3UIYmxpbmRpbmcAAAg= +0L1l/NejWE6NB2dsb2JhbHMACgJDNAOU2Bsw4lIokCYe82/5+Kg5UZH1C2leIyoe +s7dByKgU7Ciw7VXt7q5ReaTn5Z9As/lVFhBum8Euchq/flYcAfWu/DyXROeU6GFK +i0S1vsaJnIwSRgAlLBsWcK95KHCTAAAAAAAAAAD/AAAAAAAAAAZpbnB1dHMACgJD +NAOU2Bsw4lIokCYe82/5+Kg5UZH1C2leIyoes7dByKgU7Ciw7VXt7q5ReaTn5Z9A +s/lVFhBum8Euchq/flYcAfWu/DyXROeU6GFKi0S1vsaJnIwSRgAlLBsWcK95KHCT +AAAAAAAAAAD/AAAAAAAAAAthc3NpZ25tZW50cwAKAkM0A5TYGzDiUiiQJh7zb/n4 +qDlRkfULaV4jKh6zt0HIqBTsKLDtVe3urlF5pOfln0Cz+VUWEG6bwS5yGr9+VhwB +9a78PJdE55ToYUqLRLW+xomcjBJGACUsGxZwr3kocJMAAAAAAAAAAP8AAAAAAAAA +CXZhbGVuY2llcwAKAkM0A5TYGzDiUiiQJh7zb/n4qDlRkfULaV4jKh6zt0HIqBTs +KLDtVe3urlF5pOfln0Cz+VUWEG6bwS5yGr9+VhwB9a78PJdE55ToYUqLRLW+xomc +jBJGACUsGxZwr3kocJMAAAAAAAAAAP8AAAAAAAAABmVycm9ycwAJAAABAAAAAAAA +AAD/AAAAAAAAABFkZWZhdWx0QXNzaWdubWVudAAEAgAEbm9uZQAAAAEEc29tZQAF +AQJDNAOU2Bsw4lIokCYe82/5+Kg5UZH1C2leIyoes7dByKgU7Ciw7VXt7q5ReaTn +5Z9As/lVFhBum8Euchq/flYcDFZhbGVuY3lJZmFjZQYCCHJlcXVpcmVkAnuEgDye ++uIRJad8LDm8cNL96PlDrg39nPTmgu3HZspwYYYi0Xuu8GYC3+d1yYDgs2tuuugJ +DYB191E77EuT9k0IbXVsdGlwbGUCe4SAPJ764hElp3wsObxw0v3o+UOuDf2c9OaC +7cdmynBhhiLRe67wZgLf53XJgOCza2666AkNgHX3UTvsS5P2TQxWZWxvY2l0eUhp +bnQDBgt1bnNwZWNpZmllZAAGc2VsZG9tDwhlcGlzb2RpYx8HcmVndWxhcj8IZnJl +cXVlbnR/DWhpZ2hGcmVxdWVuY3n/BVZlck5vAwECdjEACFZvdXRTZWFsBgMGbWV0 +aG9kAsIre2rJPE6lpvv6/FqJcOpuDjlXp/gPPUemc5UoBqyq0lIwfH1xkDX3MH7o +KCXsG4EroYfdnZhJi0qNFvpu1UMEdm91dAL1bBNiI/Y5p0oJk9xHRsn5iqu4g1hd +tdkWPxh+xCgaCiHjPkPFqlzyKSdTozjBZ+07Y5xN2c69qY80aRe6yUN1CGJsaW5k +aW5nAAAI -----END STRICT TYPE LIB----- diff --git a/stl/RGBStd@0.1.0.stl b/stl/RGBStd@0.1.0.stl index b98f75225673df8e24feac807682e3218b4ba683..a5be69741fd2de0413353954e18f654a22be9d7f 100644 GIT binary patch delta 2158 zcmah~X;c$e7|nz%Bp3)H$`(vOilCNVL`4Y*8a)UkECSj(BqIq4nUG8ns;F#M5su4K zX~DXoxS%4A3Myh1s4L=%P_cl!)}@LlB1*G?9(sD1Kl7cL_wK##e#@)P_@f9qycB{M zcm~u7#lI-HJT~$^$)w5l%G9NeVf2uXmeMJ|JeKEJ9g1CE+gokUl?%nFG89otWk{e5 z6-v8y=KGDPEPfV)dGq;sy>G{ly6Y$i4fi@{Nr`n|P!(gW-@y~%3L{@5hywOfCMR%Oh(~`lDd1J-UJ(u0%0Zh{|{(AK!fumKUDqw(>ue zWxh>8BFNtfHU`F&ueNx)8jQo)IcAZU}>3@=y5zNaf1!=E%7Q+ALV+Gj~E zI)?RHWd|L;V;reOkh!P`i7M~~&6KeobtOwCjOWXb@#1c`bGx3-*i8eQ+zKYJ>lQdm~2nq19$V zs2o$mLTr{qg`vuH(=$~sCnd;tWuNr!9Pu$y{Vcj=rjJFdv(?C{!s8vQE9fy2Os+XW zII6-GREp8G$)-Jrnp~=KOwwlF3-KOX6-090O*)^tf5VxN0q5}7sjM(Hrch&Cl}eH* zPe$YzA4x@E86t{?WoiUZrJseIBNWknP7{xMJrHD43Rk>)#i0AuTy&h*+IaDfyzb4< zEfhTl?mKKgBuT$((`#(ML!HK~y&!#&;$LYJ|LdQhv%5n);{&gk*u~oaA0ZdRV4?V@ zu5?DeoP9LopV3_nxstBS)o++*%MsbJbvrj7E$f45Gi7K3EDKYL!1mE>9A_LOdRF8# z#})jTeP+(Z(`%QM*KWM|{Nlo7NomrBv|W!a!*GZHRv8d8rLOibS_wC6?*F}(Sgf4< zse<|Y#KLEdMq=xYmZc=BJod9cA4^COe5z5J`Zcv0lU(=MCuW^JVrO0$ndqk%_|w}(Fkuj(l` zw;t?8G%67ZFc?Ds)0++6n=}2Jh4<>yPMP_fEqLp{^`tFtUB>XjC)o!|*Q~$h@wB&4 zXg3(5KYue|3R2|X7!2+@3=K?we$F|%tFQ2Sd~3Kmy1Ok%A#pS?}`cuhNrKuPZybL_9{?`9Mhrz zh~wliRDtS=*h3M4IUF0P4-ax&K(!#xLSdzdLNitg#YnQoO9EXhc7r?H1ETUjE|N!e z6QhbcL$9avIJOpT2e)NhYJSwVa^dY=(B1*Th5$^r9|u}J*rNvjpe7=kW<@}X?HG{b z$kq~QtJ-StI2t(_!I`A@~~+DzER`OQnd-z2&do>ky-7j9r#9;xnj{?h3&Ai@xk1)7^cN}-a) z+5|&%c7z}y)5R@`^Ehj_+ada$;E<(P59Xw|#O2hN)Z{l%#rnakGhe4GV5pfrn317R zL6|~!P#q9Q>wPfP{-ELX%J)%;rP%T5nTj{vw+j7|xFd(}Y~2s@D__?+4ImjR-&~(0 zy;I+!X{^$wEnuj$4F+Tg4cE(8FfJF`zuW1=xz-!E(Ki$FdWx(Fi8KYPB&x30*=cuf zWlOl(0E!`CV2CnVB8O%DC>klib&{^QrVZ6}17L-Jv+N>S0<-NP{s961v+N?^ Y1GDibPY1JUG?NFjjywkiv%N)$2Z ^ ..0xff IfacePair} , supplements {ContractSuppl ^ ..0xff} - , assetTags {RGB.AssignmentType -> RGB.AssetTag {- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft -}} + , assetTags {RGB.AssignmentType -> ^ ..0xff RGB.AssetTag {- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft -}} , genesis RGB.Genesis {- urn:ubideco:semid:9tBmxzHdxPM1Hn5g9GgBFHnSoydLtYzgJvuSDy11tSMw#brain-adam-oliver -} , terminals {RGB.BundleId -> Terminal} , bundles [RGB.AnchoredBundle {- urn:ubideco:semid:73qjKLqMAqfv8qbNVyXjo4y2CwGWBgLUAWGamQW17ytP#eagle-jamaica-corner -} ^ ..0xffffffff] , extensions [RGB.Extension {- urn:ubideco:semid:38W6V9XrEWRP4CQBcKVTQxVwMgZGBKYGJJ2DThX73dDq#pioneer-modest-angel -} ^ ..0xffffffff] , attachments {RGB.AttachId -> [Byte ^ ..0xffffff]} , signatures {ContentId -> ^ ..0xff ContentSigs} --- urn:ubideco:semid:DQt6fWKCK67VrpJo3v9cWLeCteeKT1KEuusYEvhAo93i#concert-transit-kinetic +-- urn:ubideco:semid:9oDRVvmicJwtf2JY4FaUqMxJFUek4EJDRAvfG18qhng9#joel-byte-nuclear data Consignmenttrue :: version ContainerVer , transfer Std.Bool {- urn:ubideco:semid:7ZhBHGSJm9ixmm8Z9vCX7i5Ga7j5xrW8t11nsb1Cgpnx#laser-madam-maxwell -} , schema RGB.SchemaSchema {- urn:ubideco:semid:26TTgwB87FyCG5CJMPNpdoUdwdcEHd1STgCMiNmGqe8c#antonio-octopus-dexter -} , ifaces {IfaceId -> ^ ..0xff IfacePair} , supplements {ContractSuppl ^ ..0xff} - , assetTags {RGB.AssignmentType -> RGB.AssetTag {- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft -}} + , assetTags {RGB.AssignmentType -> ^ ..0xff RGB.AssetTag {- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft -}} , genesis RGB.Genesis {- urn:ubideco:semid:9tBmxzHdxPM1Hn5g9GgBFHnSoydLtYzgJvuSDy11tSMw#brain-adam-oliver -} , terminals {RGB.BundleId -> Terminal} , bundles [RGB.AnchoredBundle {- urn:ubideco:semid:73qjKLqMAqfv8qbNVyXjo4y2CwGWBgLUAWGamQW17ytP#eagle-jamaica-corner -} ^ ..0xffffffff] @@ -202,6 +221,8 @@ data ContentId :: schema RGB.SchemaId {- urn:ubideco:semid:AyzbMn4ux89LLU | suppl SupplId -- urn:ubideco:semid:72v5XvfiTB7HJinscrxy5ZTa4PwubG9YCtkK8JQt7F5B#denver-almanac-cobalt data ContentSigs :: {Cert ^ 1..0xa} +-- urn:ubideco:semid:CV8NDWQdTY4hy5edNiHSfNpd2M4RjXuGPjWPv58z8DSn#nurse-chris-song +data ContractIndex :: publicOpouts {RGB.Opout {- urn:ubideco:semid:Au5jXjVgXjeE2n7dFQnPQwjLRQJ3eoGygRvt8ppzXrfx#india-joshua-adam -} ^ ..0xffffff}, outpointOpouts {RGB.Output -> ^ ..0xffffff {RGB.Opout {- urn:ubideco:semid:Au5jXjVgXjeE2n7dFQnPQwjLRQJ3eoGygRvt8ppzXrfx#india-joshua-adam -} ^ ..0xffffff}} -- urn:ubideco:semid:3WPrDGfcJCVwN9ZtFUf1w6SqsutP7xuJ8wRdby9VgPHF#slang-mars-belgium data ContractSuppl :: contractId RGB.ContractId {- urn:ubideco:semid:Bho42Xw8wPy2nWxgz6H51rNdBBusaPyrVQT8VypvpZ3w#alarm-danube-vampire -} , ticker TickerSuppl @@ -226,6 +247,16 @@ data GenesisIface :: metadata StrictTypes.SemId {- urn:ubideco:semid:8Ckj2p3 data GlobalIface :: semId StrictTypes.SemId {- urn:ubideco:semid:8Ckj2p3GLKina636pSKJkj7GB6ft8XeoP4jfGkRUNwtp#cargo-plasma-catalog -}? , required Std.Bool {- urn:ubideco:semid:7ZhBHGSJm9ixmm8Z9vCX7i5Ga7j5xrW8t11nsb1Cgpnx#laser-madam-maxwell -} , multiple Std.Bool {- urn:ubideco:semid:7ZhBHGSJm9ixmm8Z9vCX7i5Ga7j5xrW8t11nsb1Cgpnx#laser-madam-maxwell -} +-- urn:ubideco:semid:Dj5dhUz7R5dprigumwNhJVuDkbDuMKKN8GVVgurArJc9#hello-carlo-roger +data Hoard :: schemata {RGB.SchemaId -> ^ ..0xff SchemaIfaces} + , ifaces {IfaceId -> ^ ..0xff Iface} + , geneses {RGB.ContractId -> ^ ..0xff RGB.Genesis {- urn:ubideco:semid:9tBmxzHdxPM1Hn5g9GgBFHnSoydLtYzgJvuSDy11tSMw#brain-adam-oliver -}} + , suppl {RGB.ContractId -> ^ ..0xff {ContractSuppl ^ ..0xff}} + , assetTags {RGB.ContractId -> ^ ..0xff {RGB.AssignmentType -> ^ ..0xff RGB.AssetTag {- urn:ubideco:semid:EZoxBpGenvb9UVze1zuwuEHqQJAqw2m3T8za5gbX1JZk#buzzer-pattern-craft -}}} + , bundles {RGB.BundleId -> ^ ..0xffffffff RGB.TransitionBundle {- urn:ubideco:semid:3Cg6aD1To23ZakSmMDxiJYDHkb34cimto3qPLJonMePt#palma-philips-enigma -}} + , extensions {RGB.OpId -> ^ ..0xffffffff RGB.Extension {- urn:ubideco:semid:38W6V9XrEWRP4CQBcKVTQxVwMgZGBKYGJJ2DThX73dDq#pioneer-modest-angel -}} + , anchors {BPCore.AnchorId -> ^ ..0xffffffff RGB.AnchorMerkleBlock {- urn:ubideco:semid:CwxenMEcD21WWNvFXXw9YvYexCNdiSHVKEe4jn3a6QxR#analog-textile-wave -}} + , sigs {ContentId -> ContentSigs} -- urn:ubideco:semid:E3ntt6pxCqzXgJoqcM3471BBPLddnKcEGsdD3mYAkz5h#ibiza-nuclear-vision data IdSuite :: pgp:0 | ssh:1 | ssi:2 @@ -262,6 +293,8 @@ data IfaceImpl :: version VerNo data IfacePair :: iface Iface, iimpl IfaceImpl -- urn:ubideco:semid:6qkzo8WTcgXTd3Ky5H2BzPppZmM5Ah4h5VrKL3oj3YTC#monday-igloo-jupiter data ImplId :: [Byte ^ 32] +-- urn:ubideco:semid:G8so7TKSnLEwX9pDP7nQSHh33H2GuXHsmPZg1ctSLLnY#natural-spain-alfred +data IndexedBundle :: RGB.ContractId {- urn:ubideco:semid:Bho42Xw8wPy2nWxgz6H51rNdBBusaPyrVQT8VypvpZ3w#alarm-danube-vampire -}, RGB.BundleId {- urn:ubideco:semid:EbWt9bmnjLpAu1LCN78snx734kHLNVUxyb5YxNr8tjd#desert-divide-visible -} -- urn:ubideco:semid:HMedi47GTVXqr27b7XTfPK5dn2VPqsgGjWK6sNbN61bU#percent-screen-pastel data NamedFieldAssignmentType :: id RGB.AssignmentType {- urn:ubideco:semid:A9sThAqgwKPfuJcR4GDfTQHUAbbS5sbEXG5XVk7FZHEg#hunter-hello-retro -} , name StrictTypes.FieldName {- urn:ubideco:semid:CK7zqXpkDGwatYDy1bVHoARAq1xTi85DFueWrZdFE7du#orient-spell-talent -} @@ -293,6 +326,16 @@ data OwnedIface :: any () data OwnedStateSuppl :: meaning [Unicode ^ ..0xff], velocity VelocityHint -- urn:ubideco:semid:5HES1VJpcPYGdN1nCiR2Xwh7NtXCLZ4DAX5kNDGAQtRh#clinic-popular-perfume data ReservedBytes04 :: [Byte ^ 4] +-- urn:ubideco:semid:DTrbbsskfwyT3nspLiicSNA2Yb8vPk9JU9GcpAEVwvWb#lorenzo-strong-golf +data SchemaIfaces :: schema RGB.SchemaSchema {- urn:ubideco:semid:26TTgwB87FyCG5CJMPNpdoUdwdcEHd1STgCMiNmGqe8c#antonio-octopus-dexter -}, iimpls {IfaceId -> ^ ..0xff IfaceImpl} +-- urn:ubideco:semid:dxDGJVLkY5pySH1VgL9QLwUL45ZPAcLMLxPoU8Dhn4W#episode-aroma-benny +data Stock :: hoard Hoard + , history {RGB.ContractId -> ^ ..0xff RGB.ContractHistory {- urn:ubideco:semid:wWuMgtgRtxV1sYctSZe4RQ5G9zdWqk913Sw9UBoeEZ1#flame-reverse-deliver -}} + , bundleOpIndex {RGB.OpId -> ^ ..0xffffff IndexedBundle} + , anchorBundleIndex {RGB.BundleId -> ^ ..0xffffff BPCore.AnchorId {- urn:ubideco:semid:HXreMRXsXhE6goE2JsF8g9jy4rZ7p7AEeYmxYgfPF2tN#dinner-single-alarm -}} + , contractIndex {RGB.ContractId -> ^ ..0xff ContractIndex} + , terminalIndex {BPCore.SecretSeal -> ^ ..0xffffff RGB.Opout {- urn:ubideco:semid:Au5jXjVgXjeE2n7dFQnPQwjLRQJ3eoGygRvt8ppzXrfx#india-joshua-adam -}} + , sealSecrets {RGB.SealDefinitionBlindSealTxPtr {- urn:ubideco:semid:67tiipcLpwkwMZTZj2rUbqZPTYWVFeCFs3U6rqc3UEdu#delphi-october-gregory -} ^ ..0xffffff} -- urn:ubideco:semid:7wqgZas6f6Y7jWyDzLNxCeGEM8NXppB1f1gZNvNHJD72#partner-austin-dinner data SupplId :: [Byte ^ 32] -- urn:ubideco:semid:CXGPwRETAtPV783GHQKZmnpvrtbUzELpBP74ScXDBP22#system-billy-polaris From d653f52f3f46c30a067008b11d912f5f5ff65c77 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 16 Nov 2023 18:29:05 +0100 Subject: [PATCH 04/12] iface: refactor builders --- src/interface/builder.rs | 278 +++++++++++++++++---------------------- 1 file changed, 122 insertions(+), 156 deletions(-) diff --git a/src/interface/builder.rs b/src/interface/builder.rs index 2262b7de..675d8e41 100644 --- a/src/interface/builder.rs +++ b/src/interface/builder.rs @@ -33,7 +33,7 @@ use strict_encoding::{FieldName, SerializeError, StrictSerialize, TypeName}; use strict_types::decode; use crate::containers::{BuilderSeal, Contract}; -use crate::interface::{Iface, IfaceImpl, IfacePair, TransitionIface, TypedState}; +use crate::interface::{Iface, IfaceImpl, IfacePair, TransitionIface}; #[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] #[display(doc_comments)] @@ -150,19 +150,7 @@ impl ContractBuilder { Ok(self) } - pub fn assignments_type(&self, name: &FieldName) -> Option { - let name = self - .builder - .iface - .genesis - .assignments - .get(name)? - .name - .as_ref() - .unwrap_or(name); - self.builder.iimpl.assignments_type(name) - } - + #[inline] pub fn add_global_state( mut self, name: impl Into, @@ -178,15 +166,7 @@ impl ContractBuilder { seal: impl Into, value: u64, ) -> Result { - let name = name.into(); - let ty = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - self.builder = self.builder.add_raw_state( - ty, - SealDefinition::Bitcoin(seal.into()), - TypedState::Amount(value), - )?; + self.builder = self.builder.add_fungible_state(name, seal, value, None)?; Ok(self) } @@ -196,18 +176,7 @@ impl ContractBuilder { seal: impl Into, value: impl StrictSerialize, ) -> Result { - let name = name.into(); - let serialized = value.to_strict_serialized::()?; - let state = RevealedData::from(serialized); - - let ty = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - self.builder = self.builder.add_raw_state( - ty, - SealDefinition::Bitcoin(seal.into()), - TypedState::Data(state), - )?; + self.builder = self.builder.add_data_state(name, seal, value, None)?; Ok(self) } @@ -289,28 +258,24 @@ impl TransitionBuilder { }) } - fn transition_iface(&self) -> &TransitionIface { - let transition_name = self - .builder - .iimpl - .transition_name(self.transition_type) - .expect("reverse type"); - self.builder - .iface - .transitions - .get(transition_name) - .expect("internal inconsistency") + #[inline] + pub fn add_asset_tag( + mut self, + assignment_type: AssignmentType, + asset_tag: AssetTag, + ) -> Result { + self.builder = self.builder.add_asset_tag(assignment_type, asset_tag)?; + Ok(self) } - pub fn assignments_type(&self, name: &FieldName) -> Option { - let name = self - .transition_iface() - .assignments - .get(name)? - .name - .as_ref() - .unwrap_or(name); - self.builder.iimpl.assignments_type(name) + #[inline] + pub fn add_global_state( + mut self, + name: impl Into, + value: impl StrictSerialize, + ) -> Result { + self.builder = self.builder.add_global_state(name, value)?; + Ok(self) } pub fn add_input(mut self, opout: Opout) -> Result { @@ -319,32 +284,20 @@ impl TransitionBuilder { } pub fn default_assignment(&self) -> Result<&FieldName, BuilderError> { - self.transition_iface() + self.builder + .transition_iface(self.transition_type) .default_assignment .as_ref() .ok_or(BuilderError::NoDefaultAssignment) } - pub fn add_global_state( - mut self, - name: impl Into, - value: impl StrictSerialize, - ) -> Result { - self.builder = self.builder.add_global_state(name, value)?; - Ok(self) - } - - pub fn add_fungible_state_default( + pub fn add_fungible_default_state( self, seal: impl Into, value: u64, ) -> Result { - let assignment_name = self.default_assignment()?; - let id = self - .assignments_type(assignment_name) - .ok_or_else(|| BuilderError::InvalidStateField(assignment_name.clone()))?; - - self.add_raw_state(id, SealDefinition::Bitcoin(seal.into()), TypedState::Amount(value)) + let assignment_name = self.default_assignment()?.clone(); + self.add_fungible_state(assignment_name, seal.into(), value) } pub fn add_fungible_state( @@ -353,15 +306,9 @@ impl TransitionBuilder { seal: impl Into, value: u64, ) -> Result { - let name = name.into(); - let ty = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - self.builder = self.builder.add_raw_state( - ty, - SealDefinition::Bitcoin(seal.into()), - TypedState::Amount(value), - )?; + self.builder = + self.builder + .add_fungible_state(name, seal, value, Some(self.transition_type))?; Ok(self) } @@ -371,28 +318,9 @@ impl TransitionBuilder { seal: impl Into, value: impl StrictSerialize, ) -> Result { - let name = name.into(); - let serialized = value.to_strict_serialized::()?; - let state = RevealedData::from(serialized); - - let ty = self - .assignments_type(&name) - .ok_or(BuilderError::AssignmentNotFound(name))?; - self.builder = self.builder.add_raw_state( - ty, - SealDefinition::Bitcoin(seal.into()), - TypedState::Data(state), - )?; - Ok(self) - } - - pub fn add_raw_state( - mut self, - type_id: AssignmentType, - seal: impl Into>, - state: TypedState, - ) -> Result { - self.builder = self.builder.add_raw_state(type_id, seal, state)?; + self.builder = + self.builder + .add_data_state(name, seal, value, Some(self.transition_type))?; Ok(self) } @@ -417,7 +345,7 @@ impl TransitionBuilder { } #[derive(Clone, Debug)] -struct OperationBuilder { +pub struct OperationBuilder { // TODO: use references instead of owned values schema: SubSchema, iface: Iface, @@ -434,7 +362,7 @@ struct OperationBuilder { } impl OperationBuilder { - pub fn with(iface: Iface, schema: SubSchema, iimpl: IfaceImpl) -> Result { + fn with(iface: Iface, schema: SubSchema, iimpl: IfaceImpl) -> Result { if iimpl.iface_id != iface.iface_id() { return Err(BuilderError::InterfaceMismatch); } @@ -458,6 +386,35 @@ impl OperationBuilder { }) } + fn transition_iface(&self, ty: TransitionType) -> &TransitionIface { + let transition_name = self.iimpl.transition_name(ty).expect("reverse type"); + self.iface + .transitions + .get(transition_name) + .expect("internal inconsistency") + } + + fn assignments_type( + &self, + name: &FieldName, + ty: Option, + ) -> Option { + let assignments = match ty { + None => &self.iface.genesis.assignments, + Some(ty) => &self.transition_iface(ty).assignments, + }; + let name = assignments.get(name)?.name.as_ref().unwrap_or(name); + self.iimpl.assignments_type(name) + } + + #[inline] + fn state_schema(&self, type_id: AssignmentType) -> &StateSchema { + self.schema + .owned_types + .get(&type_id) + .expect("schema should match interface: must be checked by the constructor") + } + #[inline] pub fn add_asset_tag( mut self, @@ -501,64 +458,73 @@ impl OperationBuilder { Ok(self) } - pub fn add_raw_state( + fn add_fungible_state( mut self, - type_id: AssignmentType, - seal: impl Into>, - state: TypedState, + name: impl Into, + seal: impl Into, + value: u64, + ty: Option, ) -> Result { - match state { - TypedState::Void => { - todo!() + let name = name.into(); + + let type_id = self + .assignments_type(&name, ty) + .ok_or(BuilderError::AssignmentNotFound(name))?; + + let tag = *self + .asset_tags + .get(&type_id) + .ok_or(BuilderError::AssetTagUnknown(type_id))?; + + let state = RevealedValue::new_random_blinding(value, tag); + + let state_schema = self.state_schema(type_id); + if *state_schema != StateSchema::Fungible(FungibleType::Unsigned64Bit) { + return Err(BuilderError::InvalidState(type_id)); + } + + let seal = BuilderSeal::::from(SealDefinition::Bitcoin(seal.into())); + match self.fungible.get_mut(&type_id) { + Some(assignments) => { + assignments.insert(seal, state)?; } - TypedState::Amount(value) => { - let tag = *self - .asset_tags - .get(&type_id) - .ok_or(BuilderError::AssetTagUnknown(type_id))?; - let state = RevealedValue::new_random_blinding(value, tag); - - let state_schema = - self.schema.owned_types.get(&type_id).expect( - "schema should match interface: must be checked by the constructor", - ); - if *state_schema != StateSchema::Fungible(FungibleType::Unsigned64Bit) { - return Err(BuilderError::InvalidState(type_id)); - } + None => { + self.fungible + .insert(type_id, Confined::with((seal, state)))?; + } + } + + Ok(self) + } - match self.fungible.get_mut(&type_id) { - Some(assignments) => { - assignments.insert(seal.into(), state)?; - } - None => { - self.fungible - .insert(type_id, Confined::with((seal.into(), state)))?; - } + fn add_data_state( + mut self, + name: impl Into, + seal: impl Into, + value: impl StrictSerialize, + ty: Option, + ) -> Result { + let name = name.into(); + let serialized = value.to_strict_serialized::()?; + let state = RevealedData::from(serialized); + + let type_id = self + .assignments_type(&name, ty) + .ok_or(BuilderError::AssignmentNotFound(name))?; + + let state_schema = self.state_schema(type_id); + let seal = BuilderSeal::::from(SealDefinition::Bitcoin(seal.into())); + if let StateSchema::Structured(_) = *state_schema { + match self.data.get_mut(&type_id) { + Some(assignments) => { + assignments.insert(seal, state)?; } - } - TypedState::Data(data) => { - let state_schema = - self.schema.owned_types.get(&type_id).expect( - "schema should match interface: must be checked by the constructor", - ); - - if let StateSchema::Structured(_) = *state_schema { - match self.data.get_mut(&type_id) { - Some(assignments) => { - assignments.insert(seal.into(), data)?; - } - None => { - self.data - .insert(type_id, Confined::with((seal.into(), data)))?; - } - } - } else { - return Err(BuilderError::InvalidState(type_id)); + None => { + self.data.insert(type_id, Confined::with((seal, state)))?; } } - TypedState::Attachment(_) => { - todo!() - } + } else { + return Err(BuilderError::InvalidState(type_id)); } Ok(self) } From a2827fb86c0a9be71f961bbba6fc70a691cb85b1 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 16 Nov 2023 19:02:36 +0100 Subject: [PATCH 05/12] iface: add information about blinding factors used in fungible state --- src/interface/builder.rs | 66 +++++++++++++++++++++++++++++----------- src/persistence/stock.rs | 5 ++- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/interface/builder.rs b/src/interface/builder.rs index 675d8e41..d1d4aa00 100644 --- a/src/interface/builder.rs +++ b/src/interface/builder.rs @@ -21,19 +21,19 @@ use std::collections::HashMap; -use amplify::confinement::{Confined, TinyOrdMap, U16, U8}; +use amplify::confinement::{Confined, TinyOrdMap, TinyOrdSet, U16, U8}; use amplify::{confinement, Wrapper}; use rgb::{ - AltLayer1, AltLayer1Set, AssetTag, Assign, AssignmentType, Assignments, ContractId, - ExposedSeal, FungibleType, Genesis, GenesisSeal, GlobalState, GraphSeal, Input, Inputs, Opout, - RevealedData, RevealedValue, SealDefinition, StateSchema, SubSchema, Transition, + AltLayer1, AltLayer1Set, AssetTag, Assign, AssignmentType, Assignments, BlindingFactor, + ContractId, ExposedSeal, FungibleType, Genesis, GenesisSeal, GlobalState, GraphSeal, Input, + Opout, RevealedData, RevealedValue, SealDefinition, StateSchema, SubSchema, Transition, TransitionType, TypedAssigns, }; use strict_encoding::{FieldName, SerializeError, StrictSerialize, TypeName}; use strict_types::decode; use crate::containers::{BuilderSeal, Contract}; -use crate::interface::{Iface, IfaceImpl, IfacePair, TransitionIface}; +use crate::interface::{Iface, IfaceImpl, IfacePair, TransitionIface, TypedState}; #[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)] #[display(doc_comments)] @@ -181,7 +181,7 @@ impl ContractBuilder { } pub fn issue_contract(self) -> Result { - let (schema, iface_pair, global, assignments, asset_tags) = self.builder.complete(); + let (schema, iface_pair, global, assignments, asset_tags) = self.builder.complete(None); let genesis = Genesis { ffv: none!(), @@ -207,7 +207,7 @@ impl ContractBuilder { pub struct TransitionBuilder { builder: OperationBuilder, transition_type: TransitionType, - inputs: Inputs, + inputs: TinyOrdMap, } impl TransitionBuilder { @@ -278,8 +278,8 @@ impl TransitionBuilder { Ok(self) } - pub fn add_input(mut self, opout: Opout) -> Result { - self.inputs.push(Input::with(opout))?; + pub fn add_input(mut self, opout: Opout, state: TypedState) -> Result { + self.inputs.insert(Input::with(opout), state)?; Ok(self) } @@ -325,7 +325,7 @@ impl TransitionBuilder { } pub fn complete_transition(self, contract_id: ContractId) -> Result { - let (_, _, global, assignments, _) = self.builder.complete(); + let (_, _, global, assignments, _) = self.builder.complete(Some(&self.inputs)); let transition = Transition { ffv: none!(), @@ -333,7 +333,9 @@ impl TransitionBuilder { transition_type: self.transition_type, metadata: empty!(), globals: global, - inputs: self.inputs, + inputs: TinyOrdSet::try_from_iter(self.inputs.into_keys()) + .expect("same size iter") + .into(), assignments, valencies: none!(), }; @@ -531,18 +533,48 @@ impl OperationBuilder { fn complete( self, + inputs: Option<&TinyOrdMap>, ) -> (SubSchema, IfacePair, GlobalState, Assignments, TinyOrdMap) { let owned_state = self.fungible.into_iter().map(|(id, vec)| { - let vec = vec.into_iter().map(|(seal, value)| match seal { - BuilderSeal::Revealed(seal) => Assign::Revealed { seal, state: value }, - BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { seal, state: value }, + let mut blindings = Vec::with_capacity(vec.len()); + let mut vec = vec + .into_iter() + .map(|(seal, value)| { + blindings.push(value.blinding); + match seal { + BuilderSeal::Revealed(seal) => Assign::Revealed { seal, state: value }, + BuilderSeal::Concealed(seal) => { + Assign::ConfidentialSeal { seal, state: value } + } + } + }) + .collect::>(); + vec.last_mut().map(|assignment| { + blindings.pop(); + let state = assignment + .as_revealed_state_mut() + .expect("builder always operates revealed state"); + let inputs = inputs + .map(|i| { + i.iter() + .filter(|(out, _)| out.prev_out.ty == id) + .map(|(_, ts)| match ts { + TypedState::Amount(_, blinding) => *blinding, + _ => panic!("previous state has invalid type"), + }) + .collect::>() + }) + .unwrap_or_default(); + state.blinding = BlindingFactor::zero_balanced(inputs, blindings).expect( + "malformed set of blinding factors; probably random generator is broken", + ); }); let state = Confined::try_from_iter(vec).expect("at least one element"); let state = TypedAssigns::Fungible(state); (id, state) }); - let owned_state_data = self.data.into_iter().map(|(id, vec)| { + let owned_data = self.data.into_iter().map(|(id, vec)| { let vec_data = vec.into_iter().map(|(seal, value)| match seal { BuilderSeal::Revealed(seal) => Assign::Revealed { seal, state: value }, BuilderSeal::Concealed(seal) => Assign::ConfidentialSeal { seal, state: value }, @@ -553,11 +585,11 @@ impl OperationBuilder { }); let owned_state = Confined::try_from_iter(owned_state).expect("same size"); - let owned_state_data = Confined::try_from_iter(owned_state_data).expect("same size"); + let owned_data = Confined::try_from_iter(owned_data).expect("same size"); let mut assignments = Assignments::from_inner(owned_state); assignments - .extend(Assignments::from_inner(owned_state_data).into_inner()) + .extend(Assignments::from_inner(owned_data).into_inner()) .expect(""); let iface_pair = IfacePair::with(self.iface, self.iimpl); diff --git a/src/persistence/stock.rs b/src/persistence/stock.rs index b9076ec2..51d1550c 100644 --- a/src/persistence/stock.rs +++ b/src/persistence/stock.rs @@ -658,7 +658,10 @@ impl Inventory for Stock { for output in history.fungibles() { if outputs.contains(&output.output) { - res.insert(output.opout, TypedState::Amount(output.state.value.as_u64())); + res.insert( + output.opout, + TypedState::Amount(output.state.value.as_u64(), output.state.blinding), + ); } } From 5a142bf6b542dc65eeabe98be012690b3bfb02c6 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 16 Nov 2023 19:02:51 +0100 Subject: [PATCH 06/12] iface: zero-balance blinding factors in operation builder --- Cargo.lock | 3 +-- Cargo.toml | 3 +++ src/interface/contract.rs | 7 +++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6ee35d1..5e21139a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -599,8 +599,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e548bc2d50dac8821ceb7fc2b8e8655cb0a41bb9d76f9b1c069d1e4e09f782c" +source = "git+https://github.com/RGB-WG/rgb-core?branch=v0.11#e81f6d3093738887cbf0bb0e7e410aef48164a5f" dependencies = [ "aluvm", "amplify", diff --git a/Cargo.toml b/Cargo.toml index 9e7dfbd0..31058b66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,3 +82,6 @@ wasm-bindgen-test = "0.3" [package.metadata.docs.rs] features = [ "all" ] + +[patch.crates-io] +rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "v0.11" } diff --git a/src/interface/contract.rs b/src/interface/contract.rs index 73da76d4..dc1c986f 100644 --- a/src/interface/contract.rs +++ b/src/interface/contract.rs @@ -25,8 +25,8 @@ use std::ops::Deref; use amplify::confinement::{LargeOrdMap, LargeVec, SmallVec}; use bp::Outpoint; use rgb::{ - AssignmentType, AttachId, ContractId, ContractState, FungibleOutput, MediaType, Output, - RevealedAttach, RevealedData, WitnessId, + AssignmentType, AttachId, BlindingFactor, ContractId, ContractState, FungibleOutput, MediaType, + Output, RevealedAttach, RevealedData, WitnessId, }; use strict_encoding::FieldName; use strict_types::typify::TypedVal; @@ -50,8 +50,7 @@ pub enum ContractError { pub enum TypedState { #[display("")] Void, - #[from] - Amount(u64), + Amount(u64, BlindingFactor), #[from] Data(RevealedData), #[from] From 4c478af9301a48c20cc16ab96445c5757ead4a32 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Thu, 16 Nov 2023 19:19:55 +0100 Subject: [PATCH 07/12] iface: introduce concept of an empty blinding factor --- Cargo.lock | 2 +- src/interface/builder.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e21139a..7fb52e90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -599,7 +599,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.1" -source = "git+https://github.com/RGB-WG/rgb-core?branch=v0.11#e81f6d3093738887cbf0bb0e7e410aef48164a5f" +source = "git+https://github.com/RGB-WG/rgb-core?branch=v0.11#8abc864deb3d907afa2644aed11f9a739c79c565" dependencies = [ "aluvm", "amplify", diff --git a/src/interface/builder.rs b/src/interface/builder.rs index d1d4aa00..509a85e9 100644 --- a/src/interface/builder.rs +++ b/src/interface/builder.rs @@ -555,7 +555,7 @@ impl OperationBuilder { let state = assignment .as_revealed_state_mut() .expect("builder always operates revealed state"); - let inputs = inputs + let mut inputs = inputs .map(|i| { i.iter() .filter(|(out, _)| out.prev_out.ty == id) @@ -566,6 +566,9 @@ impl OperationBuilder { .collect::>() }) .unwrap_or_default(); + if inputs.is_empty() { + inputs = vec![BlindingFactor::EMPTY]; + } state.blinding = BlindingFactor::zero_balanced(inputs, blindings).expect( "malformed set of blinding factors; probably random generator is broken", ); From 8fe755bae3d5168e692390d22999a8c26fe9a765 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 18 Nov 2023 12:55:20 +0100 Subject: [PATCH 08/12] stl: impl AsRef for string asset specification types --- src/stl/specs.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/stl/specs.rs b/src/stl/specs.rs index abbcad04..fc33656a 100644 --- a/src/stl/specs.rs +++ b/src/stl/specs.rs @@ -347,6 +347,11 @@ impl StrictEncode for Ticker { impl StrictSerialize for Ticker {} impl StrictDeserialize for Ticker {} +impl AsRef for Ticker { + #[inline] + fn as_ref(&self) -> &str { self.0.as_str() } +} + // TODO: Ensure all constructors filter invalid characters impl FromStr for Ticker { type Err = InvalidIdent; @@ -420,6 +425,11 @@ impl StrictEncode for Name { impl StrictSerialize for Name {} impl StrictDeserialize for Name {} +impl AsRef for Name { + #[inline] + fn as_ref(&self) -> &str { self.0.as_str() } +} + impl Name { pub fn from_strict_val_unchecked(value: &StrictVal) -> Self { Name::from_str(&value.unwrap_string()).unwrap() @@ -492,6 +502,11 @@ pub struct Details(NonEmptyString); impl StrictSerialize for Details {} impl StrictDeserialize for Details {} +impl AsRef for Details { + #[inline] + fn as_ref(&self) -> &str { self.0.as_str() } +} + impl Details { pub fn from_strict_val_unchecked(value: &StrictVal) -> Self { Details::from_str(&value.unwrap_string()).unwrap() @@ -655,6 +670,11 @@ pub struct RicardianContract(SmallString); impl StrictSerialize for RicardianContract {} impl StrictDeserialize for RicardianContract {} +impl AsRef for RicardianContract { + #[inline] + fn as_ref(&self) -> &str { self.0.as_str() } +} + impl FromStr for RicardianContract { type Err = InvalidIdent; From 1cc293cab6f0c12225690096b2c672ab903d884e Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 18 Nov 2023 12:59:08 +0100 Subject: [PATCH 09/12] iface: use verbose assignment names in builder add_asset_tag method --- src/interface/builder.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/interface/builder.rs b/src/interface/builder.rs index 509a85e9..9b0da85c 100644 --- a/src/interface/builder.rs +++ b/src/interface/builder.rs @@ -143,10 +143,10 @@ impl ContractBuilder { #[inline] pub fn add_asset_tag( mut self, - assignment_type: AssignmentType, + name: impl Into, asset_tag: AssetTag, ) -> Result { - self.builder = self.builder.add_asset_tag(assignment_type, asset_tag)?; + self.builder = self.builder.add_asset_tag(name, asset_tag, None)?; Ok(self) } @@ -261,10 +261,12 @@ impl TransitionBuilder { #[inline] pub fn add_asset_tag( mut self, - assignment_type: AssignmentType, + name: impl Into, asset_tag: AssetTag, ) -> Result { - self.builder = self.builder.add_asset_tag(assignment_type, asset_tag)?; + self.builder = self + .builder + .add_asset_tag(name, asset_tag, Some(self.transition_type))?; Ok(self) } @@ -420,10 +422,16 @@ impl OperationBuilder { #[inline] pub fn add_asset_tag( mut self, - assignment_type: AssignmentType, + name: impl Into, asset_tag: AssetTag, + ty: Option, ) -> Result { - self.asset_tags.insert(assignment_type, asset_tag)?; + let name = name.into(); + let type_id = self + .assignments_type(&name, ty) + .ok_or(BuilderError::AssignmentNotFound(name))?; + + self.asset_tags.insert(type_id, asset_tag)?; Ok(self) } From 95f70fc3b6a28b6d5c87a316a6ef3a6f2bdaf3d2 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 18 Nov 2023 12:59:24 +0100 Subject: [PATCH 10/12] iface: refactor AssetTag custom constructor --- src/interface/mod.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 2f1180ec..3b7b0910 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -73,29 +73,39 @@ mod asset_tag_ext { use bp::secp256k1::rand::{thread_rng, RngCore}; use commit_verify::{DigestExt, Sha256}; use rgb::AssetTag; + use strict_encoding::TypeName; use super::*; pub trait AssetTagExt: Sized { fn new_rgb20(issuer_domain: &str, ticker: &Ticker) -> Self { - Self::new_custom("RGB20", &format!("{issuer_domain}/{ticker}")) + Self::new_custom("RGB20", issuer_domain, ticker) } fn new_rgb21(issuer_domain: &str, ticker: &Ticker) -> Self { - Self::new_custom("RGB21", &format!("{issuer_domain}/{ticker}")) + Self::new_custom("RGB21", issuer_domain, ticker) } fn new_rgb25(issuer_domain: &str, ticker: &Ticker) -> Self { - Self::new_custom("RGB25", &format!("{issuer_domain}/{ticker}")) + Self::new_custom("RGB25", issuer_domain, ticker) } - fn new_custom(iface_name: &str, data: &str) -> Self; + fn new_custom( + iface_name: impl Into, + issuer_domain: impl AsRef, + ticker: impl AsRef, + ) -> Self; } impl AssetTagExt for AssetTag { - fn new_custom(iface_name: &str, data: &str) -> Self { + fn new_custom( + iface_name: impl Into, + issuer_domain: impl AsRef, + ticker: impl AsRef, + ) -> Self { let rand = thread_rng().next_u64(); let timestamp = SystemTime::now().elapsed().expect("system time error"); let mut hasher = Sha256::default(); - hasher.input_with_len::(iface_name.as_bytes()); - hasher.input_with_len::(data.as_bytes()); + hasher.input_with_len::(iface_name.into().as_bytes()); + hasher.input_with_len::(issuer_domain.as_ref().as_bytes()); + hasher.input_with_len::(ticker.as_ref().as_bytes()); hasher.input_raw(×tamp.as_nanos().to_le_bytes()); hasher.input_raw(&rand.to_le_bytes()); AssetTag::from(hasher.finish()) From 8ce111c9c66550d630d31e34be8e4fb2239318d7 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 18 Nov 2023 13:34:31 +0100 Subject: [PATCH 11/12] iface: abstract AssetTag from specific interface --- src/interface/mod.rs | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 3b7b0910..2ac4afc5 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -48,8 +48,6 @@ pub use rgb21::{rgb21, rgb21_stl, Rgb21, LIB_ID_RGB21, LIB_NAME_RGB21}; pub use rgb25::{rgb25, rgb25_stl, Rgb25, LIB_ID_RGB25, LIB_NAME_RGB25}; pub use suppl::{ContractSuppl, OwnedStateSuppl, SupplId, TickerSuppl, VelocityHint}; -use crate::stl::Ticker; - #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Default)] #[derive(StrictType, StrictEncode, StrictDecode)] #[strict_type(lib = crate::LIB_NAME_RGB_STD, tags = repr, into_u8, try_from_u8)] @@ -66,46 +64,26 @@ pub enum VerNo { V1 = 0, } +// TODO: Move to RGB Core mod asset_tag_ext { use std::time::SystemTime; use amplify::confinement::U8; use bp::secp256k1::rand::{thread_rng, RngCore}; use commit_verify::{DigestExt, Sha256}; - use rgb::AssetTag; - use strict_encoding::TypeName; - - use super::*; + use rgb::{AssetTag, AssignmentType}; pub trait AssetTagExt: Sized { - fn new_rgb20(issuer_domain: &str, ticker: &Ticker) -> Self { - Self::new_custom("RGB20", issuer_domain, ticker) - } - fn new_rgb21(issuer_domain: &str, ticker: &Ticker) -> Self { - Self::new_custom("RGB21", issuer_domain, ticker) - } - fn new_rgb25(issuer_domain: &str, ticker: &Ticker) -> Self { - Self::new_custom("RGB25", issuer_domain, ticker) - } - fn new_custom( - iface_name: impl Into, - issuer_domain: impl AsRef, - ticker: impl AsRef, - ) -> Self; + fn new_random(contract_domain: impl AsRef, assignment_type: AssignmentType) -> Self; } impl AssetTagExt for AssetTag { - fn new_custom( - iface_name: impl Into, - issuer_domain: impl AsRef, - ticker: impl AsRef, - ) -> Self { + fn new_random(contract_domain: impl AsRef, assignment_type: AssignmentType) -> Self { let rand = thread_rng().next_u64(); let timestamp = SystemTime::now().elapsed().expect("system time error"); let mut hasher = Sha256::default(); - hasher.input_with_len::(iface_name.into().as_bytes()); - hasher.input_with_len::(issuer_domain.as_ref().as_bytes()); - hasher.input_with_len::(ticker.as_ref().as_bytes()); + hasher.input_with_len::(contract_domain.as_ref().as_bytes()); + hasher.input_raw(&assignment_type.to_le_bytes()); hasher.input_raw(×tamp.as_nanos().to_le_bytes()); hasher.input_raw(&rand.to_le_bytes()); AssetTag::from(hasher.finish()) From 982d1be2b20121bc5a3c784b912fc8fcd49a35eb Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sat, 18 Nov 2023 14:11:36 +0100 Subject: [PATCH 12/12] chore: fix clippy lints --- src/interface/builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interface/builder.rs b/src/interface/builder.rs index 9b0da85c..5e42c587 100644 --- a/src/interface/builder.rs +++ b/src/interface/builder.rs @@ -558,7 +558,7 @@ impl OperationBuilder { } }) .collect::>(); - vec.last_mut().map(|assignment| { + if let Some(assignment) = vec.last_mut() { blindings.pop(); let state = assignment .as_revealed_state_mut() @@ -580,7 +580,7 @@ impl OperationBuilder { state.blinding = BlindingFactor::zero_balanced(inputs, blindings).expect( "malformed set of blinding factors; probably random generator is broken", ); - }); + } let state = Confined::try_from_iter(vec).expect("at least one element"); let state = TypedAssigns::Fungible(state); (id, state)