// This module is used for emitting events into the event_store. module Event { import 0x0.AddressUtil; import 0x0.BytearrayUtil; import 0x0.Hash; import 0x0.U64Util; // A resource representing the counter used to generate uniqueness under each account. There won't be destructor for // this resource to guarantee the uniqueness of the generated handle. resource HandleIdGenerator { // A monotonically increasing counter counter: u64, } // A handle for a event such that: // 1. Other modules can emit event to this handle. // 2. Storage can use this handle to prove the total number of events that happened in the past. resource Handle { // Total number of events emitted to this event stream. counter: u64 // A globally unique ID for this event stream. guid: bytearray, } // Derive a fresh unique id by using sender's HandleIdGenerator. The generated bytearray is indeed unique because it // was derived from the hash(sender's HandleIdGenerator || sender_address). This module guarantees that the // HandleIdGenerator is only going to be monotonically increased and there's no way to revert it or destroy it. Thus // such counter is going to give distinct value for each of the new event stream under each sender. And since we // hash it with the sender's address, the result is guaranteed to be globally unique. fresh_guid(): bytearray acquires HandleIdGenerator { let exists_counter: bool; let sender: address; let counter: &mut Self.HandleIdGenerator; let count: &mut u64; let sender_bytes: bytearray; let count_bytes: bytearray; let preimage: bytearray; sender = get_txn_sender(); exists_counter = exists(copy(sender)); if (!move(exists_counter)) { move_to_sender(HandleIdGenerator { counter: 0 }); } counter = borrow_global_mut(copy(sender)); count = &mut move(counter).counter; sender_bytes = AddressUtil.address_to_bytes(move(sender)); count_bytes = U64Util.u64_to_bytes(*copy(count)); *move(count) = *copy(count) + 1; // HandleIdGenerator goes first just in case we want to extend address in the future. preimage = BytearrayUtil.bytearray_concat(move(count_bytes), move(sender_bytes)); return Hash.sha3_256(move(preimage)); } // Use the sender's HandleIdGenerator to generate a unique event handle that one can emit an event to. public new_event_handle(): Self.Handle acquires HandleIdGenerator { return Handle {counter: 0, guid: Self.fresh_guid()}; } // Emit an event with payload `msg` by using handle's key and counter. Will change the payload from bytearray to a // generic type parameter once we have generics. public emit_event(handle_ref: &mut Self.Handle, msg: T) { let count: &mut u64; let guid: bytearray; guid = *©(handle_ref).guid; count = &mut move(handle_ref).counter; Self.write_to_event_store(move(guid), *copy(count), move(msg)); *move(count) = *copy(count) + 1; return; } // Native procedure that writes to the actual event stream in Event store // This will replace the "native" portion of EmitEvent bytecode native write_to_event_store(guid: bytearray, count: u64, msg: T); // Destroy a unique handle. public destroy(handle: Self.Handle) { let guid: bytearray; let count: u64; Handle { count, guid } = move(handle); return; } // TODO: We might want getters for reading the counter / id given a reference to a handle. }