// Matrix Construct // // Copyright (C) Matrix Construct Developers, Authors & Contributors // Copyright (C) 2016-2018 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #pragma once #define HAVE_IRCD_M_VM_H /// Matrix Virtual Machine /// namespace ircd::m::vm { struct error; // custom exception struct opts; struct copts; struct eval; struct phase; struct accepted; enum fault :uint; using fault_t = std::underlying_type::type; extern log::log log; extern ctx::shared_view accept; extern uint64_t current_sequence; extern const opts default_opts; extern const copts default_copts; string_view reflect(const fault &); const uint64_t &sequence(const eval &); uint64_t retired_sequence(id::event::buf &); uint64_t retired_sequence(); } /// Event Evaluation Device /// /// This object conducts the evaluation of an event or a tape of multiple /// events. An event is evaluated in an attempt to execute it. Events which /// fail during evaluation won't be executed; such is the case for events which /// have already been executed, or events which are invalid or lead to invalid /// transitions or actions of the machine etc. /// struct ircd::m::vm::eval :instance_list { static uint64_t id_ctr; // monotonic const vm::opts *opts {&default_opts}; const vm::copts *copts {nullptr}; ctx::ctx *ctx {ctx::current}; uint64_t id {++id_ctr}; string_view room_id; const json::iov *issue {nullptr}; const event *event_ {nullptr}; vm::phase *phase {nullptr}; uint64_t sequence {0}; db::txn *txn {nullptr}; event::id::buf event_id; public: operator const event::id::buf &() const; fault operator()(const event &); fault operator()(json::iov &event, const json::iov &content); fault operator()(const room &, json::iov &event, const json::iov &content); eval(const vm::opts &); eval(const vm::copts &); eval(const event &, const vm::opts & = default_opts); eval(json::iov &event, const json::iov &content, const vm::copts & = default_copts); eval(const room &, json::iov &event, const json::iov &content); eval() = default; eval(eval &&) = delete; eval(const eval &) = delete; ~eval() noexcept; }; /// Evaluation faults. These are reasons which evaluation has halted but may /// continue after the user defaults the fault. They are basically types of /// interrupts and traps, which are supposed to be recoverable. Only the /// GENERAL protection fault (#gp) is an abort and is not supposed to be /// recoverable. The fault codes have the form of bitflags so they can be /// used in masks; outside of that case only one fault is dealt with at /// a time so they can be switched as they appear in the enum. /// enum ircd::m::vm::fault :uint { ACCEPT = 0x00, ///< No fault. EXISTS = 0x01, ///< Replaying existing event. (#ex) INVALID = 0x02, ///< Non-conforming event format. (#ud) DEBUGSTEP = 0x04, ///< Debug step. (#db) BREAKPOINT = 0x08, ///< Debug breakpoint. (#bp) GENERAL = 0x10, ///< General protection fault. (#gp) EVENT = 0x20, ///< Eval requires addl events in the ef register (#ef) STATE = 0x40, ///< Required state is missing (#st) INTERRUPT = 0x80, ///< ctx::interrupted (#nmi) }; /// Evaluation Options struct ircd::m::vm::opts { /// Make writes to database bool write {true}; /// Apply effects of the eval bool effects {true}; /// Broadcast to clients/servers. When true, individual notify options /// that follow are considered. When false, no notifications occur. short notify {true}; /// Broadcast to local clients (/sync stream). bool notify_clients {true}; /// Broadcast to federation servers (/federation/send/). bool notify_servers {true}; /// False to allow a dirty conforms report (not recommended). bool conforming {true}; /// Mask of conformity failures to allow without considering dirty. event::conforms non_conform; /// If the event was already checked before the eval, set this to true /// and include the report (see below). bool conformed {false}; /// When conformed=true, this report will be included instead of generating /// one during the eval. This is useful if a conformity check was already /// done before eval. event::conforms report; /// Toggles whether event may be considered a "present event" and may /// update the optimized present state table of the room if it is proper. bool present {true}; /// Toggles whether event may be added to the room head table which means /// it is considered unreferenced by any other event at this time. It is /// safe for this to always be true if events are evaluated in order. If /// `present` is false this should be set to false but they are not tied. bool head {true}; /// Toggles whether the prev_events of this event are removed from the /// room head table, now that this event has referenced them. It is safe /// for this to always be true. bool refs {true}; /// Toggles whether the state btree is updated; this should be consistently /// true or false for all events in a room. bool history {true}; /// Bypass check for event having already been evaluated so it can be /// replayed through the system (not recommended). bool replays {false}; // Verify the origin signature bool verify {true}; /// TODO: Y bool prev_check_exists {true}; /// TODO: Y bool head_must_exist {false}; /// Evaluators can set this value to optimize the creation of the database /// transaction where the event will be stored. This value should be set /// to the amount of space the event consumes; the JSON-serialized size is /// a good value here. Default of -1 will automatically use serialized(). size_t reserve_bytes = -1; /// This value is added to reserve_bytes to account for indexing overhead /// in the database transaction allocation. Most evaluators have little /// reason to ever adjust this. size_t reserve_index {1024}; /// Mask of faults that are not thrown as exceptions out of eval(). If /// masked, the fault is returned from eval(). By default, the EXISTS /// fault is masked which means existing events won't kill eval loops /// as well as the debug related. fault_t nothrows { EXISTS | DEBUGSTEP | BREAKPOINT }; /// Mask of faults that are logged to the error facility in vm::log. fault_t errorlog { ~(EXISTS | DEBUGSTEP | BREAKPOINT) }; /// Mask of faults that are logged to the warning facility in vm::log fault_t warnlog { EXISTS }; /// Whether to log a debug message on successful eval. bool debuglog_accept {false}; /// Whether to log an info message on successful eval. bool infolog_accept {false}; }; /// Extension structure to vm::opts which includes additional options for /// commissioning events originating from this server which are then passed /// through eval (this process is committing). struct ircd::m::vm::copts :opts { /// Hash and include hashes object. bool add_hash {true}; /// Sign and include signatures object bool add_sig {true}; /// Generate and include event_id bool add_event_id {true}; /// Include our origin bool add_origin {true}; /// Include origin_server_ts bool add_origin_server_ts {true}; /// Add prev_events bool add_prev_events {true}; /// Add prev_state bool add_prev_state {true}; /// Add auth_events bool add_auth_events {true}; /// Whether to log a debug message before commit bool debuglog_precommit {false}; /// Whether to log an info message after commit accepted bool infolog_postcommit {false}; }; struct ircd::m::vm::phase :instance_list { string_view name; phase(const string_view &name); }; struct ircd::m::vm::accepted :m::event { ctx::ctx *context; const vm::opts *opts; const event::conforms *report; shared_buffer strung; accepted(const m::event &event, const vm::opts *const &opts, const event::conforms *const &report); }; struct ircd::m::vm::error :m::error { vm::fault code; template error(const fault &code, const char *const &fmt, args&&... a); template error(const char *const &fmt, args&&... a); }; template ircd::m::vm::error::error(const fault &code, const char *const &fmt, args&&... a) :m::error { http::NOT_MODIFIED, "M_VM_FAULT", fmt, std::forward(a)... } ,code { code } {} template ircd::m::vm::error::error(const char *const &fmt, args&&... a) :m::error { http::INTERNAL_SERVER_ERROR, "M_VM_FAULT", fmt, std::forward(a)... } ,code { fault::GENERAL } {}