mirror of
https://github.com/matrix-construct/construct
synced 2024-11-01 03:18:54 +01:00
1306 lines
22 KiB
C++
1306 lines
22 KiB
C++
// Matrix Construct
|
|
//
|
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
|
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
|
//
|
|
// 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.
|
|
|
|
namespace ircd::m
|
|
{
|
|
static json::object make_hashes(const mutable_buffer &out, const sha256::buf &hash);
|
|
}
|
|
|
|
/// The maximum size of an event we will create. This may also be used in
|
|
/// some contexts for what we will accept, but the protocol limit and hard
|
|
/// worst-case buffer size is still event::MAX_SIZE.
|
|
decltype(ircd::m::event::max_size)
|
|
ircd::m::event::max_size
|
|
{
|
|
{ "name", "m.event.max_size" },
|
|
{ "default", 65507L },
|
|
};
|
|
|
|
decltype(ircd::m::event::buf)
|
|
thread_local
|
|
ircd::m::event::buf;
|
|
|
|
bool
|
|
ircd::m::check_id(const event &event)
|
|
noexcept
|
|
{
|
|
if(!event.event_id)
|
|
return false;
|
|
|
|
const string_view &version
|
|
{
|
|
event.event_id.version()
|
|
};
|
|
|
|
return check_id(event, version);
|
|
}
|
|
|
|
bool
|
|
ircd::m::check_id(const event &event,
|
|
const string_view &room_version)
|
|
noexcept try
|
|
{
|
|
assert(event.event_id);
|
|
const auto &version
|
|
{
|
|
room_version?: event.event_id.version()
|
|
};
|
|
|
|
char buf[64];
|
|
const event::id &check_id
|
|
{
|
|
version == "1" || version == "2"?
|
|
event::id{json::get<"event_id"_>(event)}:
|
|
|
|
version == "3"?
|
|
event::id{event::id::v3{buf, event}}:
|
|
|
|
event::id{event::id::v4{buf, event}}
|
|
};
|
|
|
|
return event.event_id == check_id;
|
|
}
|
|
catch(const std::exception &e)
|
|
{
|
|
log::error
|
|
{
|
|
"m::check_id() :%s", e.what()
|
|
};
|
|
|
|
return false;
|
|
}
|
|
catch(...)
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
ircd::m::id::event
|
|
ircd::m::make_id(const event &event,
|
|
const string_view &version,
|
|
id::event::buf &buf)
|
|
{
|
|
if(version == "1" || version == "2")
|
|
{
|
|
const crh::sha256::buf hash{event};
|
|
return make_id(event, version, buf, hash);
|
|
}
|
|
|
|
if(version == "3")
|
|
return event::id::v3
|
|
{
|
|
buf, event
|
|
};
|
|
|
|
return event::id::v4
|
|
{
|
|
buf, event
|
|
};
|
|
}
|
|
|
|
ircd::m::id::event
|
|
ircd::m::make_id(const event &event,
|
|
const string_view &version,
|
|
id::event::buf &buf,
|
|
const const_buffer &hash)
|
|
{
|
|
char readable[b64::encode_size(sha256::digest_size)];
|
|
|
|
if(version == "1" || version == "2")
|
|
{
|
|
const id::event ret
|
|
{
|
|
buf, b64::encode_unpadded<b64::urlsafe>(readable, hash), at<"origin"_>(event)
|
|
};
|
|
|
|
buf.assigned(ret);
|
|
return ret;
|
|
}
|
|
else if(version == "3")
|
|
{
|
|
const id::event ret
|
|
{
|
|
buf, b64::encode_unpadded(readable, hash), string_view{}
|
|
};
|
|
|
|
buf.assigned(ret);
|
|
return ret;
|
|
}
|
|
|
|
const id::event ret
|
|
{
|
|
buf, b64::encode_unpadded<b64::urlsafe>(readable, hash), string_view{}
|
|
};
|
|
|
|
buf.assigned(ret);
|
|
return ret;
|
|
}
|
|
|
|
ircd::json::object
|
|
ircd::m::hashes(const mutable_buffer &out,
|
|
const event &event)
|
|
{
|
|
const sha256::buf hash_
|
|
{
|
|
hash(event)
|
|
};
|
|
|
|
return make_hashes(out, hash_);
|
|
}
|
|
|
|
ircd::json::object
|
|
ircd::m::event::hashes(const mutable_buffer &out,
|
|
json::iov &event,
|
|
const string_view &content)
|
|
{
|
|
const sha256::buf hash_
|
|
{
|
|
hash(event, content)
|
|
};
|
|
|
|
return make_hashes(out, hash_);
|
|
}
|
|
|
|
ircd::json::object
|
|
ircd::m::make_hashes(const mutable_buffer &out,
|
|
const sha256::buf &hash)
|
|
{
|
|
static const auto b64bufsz
|
|
{
|
|
b64::encode_size(sizeof(hash))
|
|
};
|
|
|
|
char hashb64buf[b64bufsz];
|
|
const json::members hashes
|
|
{
|
|
{ "sha256", b64::encode_unpadded(hashb64buf, hash) }
|
|
};
|
|
|
|
return json::stringify(mutable_buffer{out}, hashes);
|
|
}
|
|
|
|
ircd::sha256::buf
|
|
ircd::m::event::hash(const json::object &event_)
|
|
{
|
|
const json::object preimage
|
|
{
|
|
event::preimage(buf[3], event_)
|
|
};
|
|
|
|
return sha256
|
|
{
|
|
preimage
|
|
};
|
|
}
|
|
|
|
ircd::sha256::buf
|
|
ircd::m::event::hash(json::iov &event,
|
|
const string_view &content)
|
|
{
|
|
const json::iov::push _content
|
|
{
|
|
event, { "content", content }
|
|
};
|
|
|
|
return m::hash(m::event{event});
|
|
}
|
|
|
|
ircd::sha256::buf
|
|
ircd::m::hash(const event &event)
|
|
{
|
|
if(event.source)
|
|
return event::hash(event.source);
|
|
|
|
m::event event_{event};
|
|
json::get<"signatures"_>(event_) = {};
|
|
json::get<"hashes"_>(event_) = {};
|
|
const string_view preimage
|
|
{
|
|
stringify(event::buf[3], event_)
|
|
};
|
|
|
|
return sha256{preimage};
|
|
}
|
|
|
|
bool
|
|
ircd::m::verify_hash(const event &event)
|
|
{
|
|
const sha256::buf hash
|
|
{
|
|
m::hash(event)
|
|
};
|
|
|
|
return verify_hash(event, hash);
|
|
}
|
|
|
|
bool
|
|
ircd::m::verify_hash(const event &event,
|
|
const sha256::buf &actual)
|
|
try
|
|
{
|
|
const json::object &object
|
|
{
|
|
at<"hashes"_>(event)
|
|
};
|
|
|
|
const json::string &hash
|
|
{
|
|
object.at("sha256")
|
|
};
|
|
|
|
char buf[32];
|
|
const auto claim
|
|
{
|
|
b64::decode(buf, hash)
|
|
};
|
|
|
|
static_assert(sizeof(buf) == sizeof(actual));
|
|
if(unlikely(ircd::size(claim) != sizeof(actual)))
|
|
return false;
|
|
|
|
if(memcmp(buf, ircd::data(actual), sizeof(buf)) != 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
catch(const json::not_found &)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ircd::json::object
|
|
ircd::m::event::signatures(const mutable_buffer &out,
|
|
json::iov &event,
|
|
const json::iov &content)
|
|
{
|
|
const string_view &origin
|
|
{
|
|
event.at("origin")
|
|
};
|
|
|
|
const ed25519::sig sig
|
|
{
|
|
sign(event, content)
|
|
};
|
|
|
|
const string_view public_key_id
|
|
{
|
|
m::public_key_id(m::my(origin))
|
|
};
|
|
|
|
char sigb64buf[b64::encode_size(sizeof(sig))];
|
|
const json::members sigb64
|
|
{
|
|
{ public_key_id, b64::encode_unpadded(sigb64buf, sig) }
|
|
};
|
|
|
|
const json::members sigs
|
|
{
|
|
{ origin, sigb64 }
|
|
};
|
|
|
|
return json::stringify(mutable_buffer{out}, sigs);
|
|
}
|
|
|
|
ircd::m::event
|
|
ircd::m::signatures(const mutable_buffer &out,
|
|
const m::event &event)
|
|
{
|
|
const string_view &origin
|
|
{
|
|
at<"origin"_>(event)
|
|
};
|
|
|
|
return signatures(out, event, origin);
|
|
}
|
|
|
|
ircd::m::event
|
|
ircd::m::signatures(const mutable_buffer &out_,
|
|
const m::event &event_,
|
|
const string_view &origin)
|
|
{
|
|
m::event event
|
|
{
|
|
essential(event_, event::buf[3])
|
|
};
|
|
|
|
const string_view &preimage
|
|
{
|
|
stringify(event::buf[2], event)
|
|
};
|
|
|
|
const auto &secret_key
|
|
{
|
|
m::secret_key(my(origin))
|
|
};
|
|
|
|
const string_view public_key_id
|
|
{
|
|
m::public_key_id(my(origin))
|
|
};
|
|
|
|
const ed25519::sig my_sig
|
|
{
|
|
event::sign(preimage, secret_key)
|
|
};
|
|
|
|
static const auto sigb64bufsz
|
|
{
|
|
b64::encode_size(sizeof(my_sig))
|
|
};
|
|
|
|
char sigb64buf[sigb64bufsz];
|
|
const json::member my_sig_member
|
|
{
|
|
origin, json::members
|
|
{
|
|
{ public_key_id, b64::encode_unpadded(sigb64buf, my_sig) }
|
|
}
|
|
};
|
|
|
|
static const size_t SIG_MAX{64};
|
|
thread_local std::array<json::member, SIG_MAX> sigs;
|
|
|
|
size_t i(0);
|
|
sigs.at(i++) = my_sig_member;
|
|
for(const auto &[host, sig] : json::get<"signatures"_>(event_))
|
|
if(json::string(host) != origin)
|
|
sigs.at(i++) = { host, sig };
|
|
|
|
event = event_;
|
|
mutable_buffer out{out_};
|
|
json::get<"signatures"_>(event) = json::stringify(out, sigs.data(), sigs.data() + i);
|
|
return event;
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::event::sign(json::iov &event,
|
|
const json::iov &contents)
|
|
{
|
|
const string_view &origin
|
|
{
|
|
event.at("origin")
|
|
};
|
|
|
|
const auto &secret_key
|
|
{
|
|
m::secret_key(m::my(origin))
|
|
};
|
|
|
|
return sign(event, contents, secret_key);
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::event::sign(json::iov &event,
|
|
const json::iov &contents,
|
|
const ed25519::sk &sk)
|
|
{
|
|
ed25519::sig sig;
|
|
essential(event, contents, [&sk, &sig]
|
|
(json::iov &event)
|
|
{
|
|
sig = m::sign(m::event{event}, sk);
|
|
});
|
|
|
|
return sig;
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::sign(const event &event)
|
|
{
|
|
const string_view &origin
|
|
{
|
|
at<"origin"_>(event)
|
|
};
|
|
|
|
return sign(event, origin);
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::sign(const event &event,
|
|
const string_view &origin)
|
|
{
|
|
const auto &secret_key
|
|
{
|
|
m::secret_key(my(origin))
|
|
};
|
|
|
|
return sign(event, secret_key);
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::sign(const event &event,
|
|
const ed25519::sk &sk)
|
|
{
|
|
const string_view preimage
|
|
{
|
|
stringify(event::buf[3], event)
|
|
};
|
|
|
|
return event::sign(preimage, sk);
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::event::sign(const json::object &event)
|
|
{
|
|
const json::string &origin
|
|
{
|
|
event.at("origin")
|
|
};
|
|
|
|
const auto &secret_key
|
|
{
|
|
m::secret_key(m::my(origin))
|
|
};
|
|
|
|
return sign(event, secret_key);
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::event::sign(const json::object &event,
|
|
const ed25519::sk &sk)
|
|
{
|
|
//TODO: skip rewrite
|
|
const string_view preimage
|
|
{
|
|
stringify(buf[3], event)
|
|
};
|
|
|
|
return sign(preimage, sk);
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::event::sign(const string_view &event)
|
|
{
|
|
const json::string &origin
|
|
{
|
|
json::object(event).at("origin")
|
|
};
|
|
|
|
const auto &secret_key
|
|
{
|
|
m::secret_key(m::my(origin))
|
|
};
|
|
|
|
return sign(event, secret_key);
|
|
}
|
|
|
|
ircd::ed25519::sig
|
|
ircd::m::event::sign(const string_view &event,
|
|
const ed25519::sk &sk)
|
|
{
|
|
const ed25519::sig sig
|
|
{
|
|
sk.sign(event)
|
|
};
|
|
|
|
return sig;
|
|
}
|
|
bool
|
|
ircd::m::verify(const event &event)
|
|
{
|
|
const string_view &origin
|
|
{
|
|
at<"origin"_>(event)
|
|
};
|
|
|
|
return verify(event, origin);
|
|
}
|
|
|
|
bool
|
|
ircd::m::verify(const event &event,
|
|
const string_view &origin)
|
|
{
|
|
const json::object &signatures
|
|
{
|
|
at<"signatures"_>(event)
|
|
};
|
|
|
|
const json::object &origin_sigs
|
|
{
|
|
signatures.at(origin)
|
|
};
|
|
|
|
for(const auto &[keyid, sig] : origin_sigs)
|
|
if(verify(event, origin, json::string(keyid)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
ircd::m::verify(const event &event,
|
|
const string_view &origin,
|
|
const string_view &keyid)
|
|
try
|
|
{
|
|
const m::node::keys node_keys
|
|
{
|
|
origin
|
|
};
|
|
|
|
bool ret{false};
|
|
node_keys.get(keyid, [&ret, &event, &origin, &keyid]
|
|
(const ed25519::pk &pk)
|
|
{
|
|
ret = verify(event, pk, origin, keyid);
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
catch(const ctx::interrupted &e)
|
|
{
|
|
log::error
|
|
{
|
|
log, "Failed to verify %s because key %s for %s :%s",
|
|
string_view{event.event_id},
|
|
keyid,
|
|
origin,
|
|
e.what(),
|
|
};
|
|
|
|
throw;
|
|
}
|
|
catch(const std::exception &e)
|
|
{
|
|
throw m::error
|
|
{
|
|
http::UNAUTHORIZED, "M_UNVERIFIABLE_SIGNATURE",
|
|
"%s key %s for %s :%s",
|
|
string_view{event.event_id},
|
|
keyid,
|
|
origin,
|
|
e.what(),
|
|
};
|
|
}
|
|
|
|
bool
|
|
ircd::m::verify(const event &event,
|
|
const ed25519::pk &pk,
|
|
const string_view &origin,
|
|
const string_view &keyid)
|
|
{
|
|
const json::object &signatures
|
|
{
|
|
at<"signatures"_>(event)
|
|
};
|
|
|
|
const json::object &origin_sigs
|
|
{
|
|
signatures.at(origin)
|
|
};
|
|
|
|
const ed25519::sig sig
|
|
{
|
|
[&origin_sigs, &keyid](auto&& buf)
|
|
{
|
|
b64::decode(buf, json::string(origin_sigs.at(keyid)));
|
|
}
|
|
};
|
|
|
|
return verify(event, pk, sig);
|
|
}
|
|
|
|
bool
|
|
ircd::m::verify(const event &event_,
|
|
const ed25519::pk &pk,
|
|
const ed25519::sig &sig)
|
|
{
|
|
m::event event
|
|
{
|
|
essential(event_, event::buf[3])
|
|
};
|
|
|
|
const json::object &preimage
|
|
{
|
|
stringify(event::buf[2], event)
|
|
};
|
|
|
|
return pk.verify(preimage, sig);
|
|
}
|
|
|
|
bool
|
|
ircd::m::event::verify(const json::object &event,
|
|
const ed25519::pk &pk,
|
|
const ed25519::sig &sig)
|
|
{
|
|
const string_view preimage
|
|
{
|
|
stringify(buf[3], event)
|
|
};
|
|
|
|
return pk.verify(preimage, sig);
|
|
}
|
|
|
|
void
|
|
ircd::m::event::essential(json::iov &event,
|
|
const json::iov &contents,
|
|
const event::closure_iov_mutable &closure)
|
|
try
|
|
{
|
|
const auto &type
|
|
{
|
|
event.at("type")
|
|
};
|
|
|
|
if(type == "m.room.aliases")
|
|
{
|
|
const json::iov::push _content{event,
|
|
{
|
|
"content", json::members
|
|
{
|
|
{ "aliases", contents.at("aliases") }
|
|
}
|
|
}};
|
|
|
|
closure(event);
|
|
}
|
|
else if(type == "m.room.create")
|
|
{
|
|
const json::iov::push _content{event,
|
|
{
|
|
"content", json::members
|
|
{
|
|
{ "creator", contents.at("creator") }
|
|
}
|
|
}};
|
|
|
|
closure(event);
|
|
}
|
|
else if(type == "m.room.history_visibility")
|
|
{
|
|
const json::iov::push _content{event,
|
|
{
|
|
"content", json::members
|
|
{
|
|
{ "history_visibility", contents.at("history_visibility") }
|
|
}
|
|
}};
|
|
|
|
closure(event);
|
|
}
|
|
else if(type == "m.room.join_rules")
|
|
{
|
|
const json::iov::push _content{event,
|
|
{
|
|
"content", json::members
|
|
{
|
|
{ "join_rule", contents.at("join_rule") }
|
|
}
|
|
}};
|
|
|
|
closure(event);
|
|
}
|
|
else if(type == "m.room.member")
|
|
{
|
|
const json::iov::push _content{event,
|
|
{
|
|
"content", json::members
|
|
{
|
|
{ "membership", contents.at("membership") }
|
|
}
|
|
}};
|
|
|
|
closure(event);
|
|
}
|
|
else if(type == "m.room.power_levels")
|
|
{
|
|
const json::iov::push _content{event,
|
|
{
|
|
"content", json::members
|
|
{
|
|
{ "ban", contents.at("ban") },
|
|
{ "events", contents.at("events") },
|
|
{ "events_default", contents.at("events_default") },
|
|
{ "kick", contents.at("kick") },
|
|
{ "redact", contents.at("redact") },
|
|
{ "state_default", contents.at("state_default") },
|
|
{ "users", contents.at("users") },
|
|
{ "users_default", contents.at("users_default") },
|
|
}
|
|
}};
|
|
|
|
closure(event);
|
|
}
|
|
else if(type == "m.room.redaction")
|
|
{
|
|
// This simply finds the redacts key and swaps it with jsundefined for
|
|
// the scope's duration. The redacts key will still be present and
|
|
// visible in the json::iov which is incorrect if directly serialized.
|
|
// However, this iov is turned into a json::tuple (m::event) which ends
|
|
// up being serialized for signing. That serialization is where the
|
|
// jsundefined redacts value is ignored.
|
|
auto &redacts{event.at("redacts")};
|
|
json::value temp(std::move(redacts));
|
|
redacts = json::value{};
|
|
const unwind _{[&redacts, &temp]
|
|
{
|
|
redacts = std::move(temp);
|
|
}};
|
|
|
|
const json::iov::push _content
|
|
{
|
|
event, { "content", "{}" }
|
|
};
|
|
|
|
closure(event);
|
|
}
|
|
else
|
|
{
|
|
const json::iov::push _content
|
|
{
|
|
event, { "content", "{}" }
|
|
};
|
|
|
|
closure(event);
|
|
}
|
|
}
|
|
catch(const json::not_found &e)
|
|
{
|
|
log::derror
|
|
{
|
|
log, "Error while isolating essential keys (redaction algorithm) :%s",
|
|
e.what(),
|
|
};
|
|
|
|
throw;
|
|
}
|
|
|
|
ircd::m::event
|
|
ircd::m::essential(m::event event,
|
|
const mutable_buffer &contentbuf)
|
|
try
|
|
{
|
|
const auto &type
|
|
{
|
|
json::get<"type"_>(event)
|
|
};
|
|
|
|
json::object &content
|
|
{
|
|
json::get<"content"_>(event)
|
|
};
|
|
|
|
mutable_buffer essential
|
|
{
|
|
contentbuf
|
|
};
|
|
|
|
if(type == "m.room.aliases")
|
|
{
|
|
if(content.has("aliases"))
|
|
content = json::stringify(essential, json::members
|
|
{
|
|
{ "aliases", content.at("aliases") }
|
|
});
|
|
}
|
|
else if(type == "m.room.create")
|
|
{
|
|
if(content.has("creator"))
|
|
content = json::stringify(essential, json::members
|
|
{
|
|
{ "creator", content.at("creator") }
|
|
});
|
|
}
|
|
else if(type == "m.room.history_visibility")
|
|
{
|
|
if(content.has("history_visibility"))
|
|
content = json::stringify(essential, json::members
|
|
{
|
|
{ "history_visibility", content.at("history_visibility") }
|
|
});
|
|
}
|
|
else if(type == "m.room.join_rules")
|
|
{
|
|
if(content.has("join_rule"))
|
|
content = json::stringify(essential, json::members
|
|
{
|
|
{ "join_rule", content.at("join_rule") }
|
|
});
|
|
}
|
|
else if(type == "m.room.member")
|
|
{
|
|
if(content.has("membership"))
|
|
content = json::stringify(essential, json::members
|
|
{
|
|
{ "membership", content.at("membership") }
|
|
});
|
|
}
|
|
else if(type == "m.room.power_levels")
|
|
{
|
|
json::stack out{essential};
|
|
json::stack::object top{out};
|
|
|
|
if(content.has("ban"))
|
|
json::stack::member
|
|
{
|
|
top, "ban", content.at("ban")
|
|
};
|
|
|
|
if(content.has("events"))
|
|
json::stack::member
|
|
{
|
|
top, "events", content.at("events")
|
|
};
|
|
|
|
if(content.has("events_default"))
|
|
json::stack::member
|
|
{
|
|
top, "events_default", content.at("events_default")
|
|
};
|
|
|
|
if(content.has("kick"))
|
|
json::stack::member
|
|
{
|
|
top, "kick", content.at("kick")
|
|
};
|
|
|
|
if(content.has("redact"))
|
|
json::stack::member
|
|
{
|
|
top, "redact", content.at("redact")
|
|
};
|
|
|
|
if(content.has("state_default"))
|
|
json::stack::member
|
|
{
|
|
top, "state_default", content.at("state_default")
|
|
};
|
|
|
|
if(content.has("users"))
|
|
json::stack::member
|
|
{
|
|
top, "users", content.at("users")
|
|
};
|
|
|
|
if(content.has("users_default"))
|
|
json::stack::member
|
|
{
|
|
top, "users_default", content.at("users_default")
|
|
};
|
|
|
|
top.~object();
|
|
content = out.completed();
|
|
}
|
|
else if(type == "m.room.redaction")
|
|
{
|
|
json::get<"redacts"_>(event) = string_view{};
|
|
content = "{}"_sv;
|
|
}
|
|
else
|
|
{
|
|
content = "{}"_sv;
|
|
}
|
|
|
|
json::get<"signatures"_>(event) = {};
|
|
return event;
|
|
}
|
|
catch(const json::not_found &e)
|
|
{
|
|
log::derror
|
|
{
|
|
log, "Error while isolating essential keys (redaction algorithm) :%s",
|
|
e.what(),
|
|
};
|
|
|
|
throw;
|
|
}
|
|
|
|
ircd::json::object
|
|
ircd::m::event::preimage(const mutable_buffer &buf_,
|
|
const json::object &event)
|
|
try
|
|
{
|
|
static const size_t iov_max{json::iov::max_size};
|
|
thread_local std::array<json::object::member, iov_max> member;
|
|
|
|
size_t i(0);
|
|
for(const auto &m : event)
|
|
{
|
|
if(m.first == "signatures" ||
|
|
m.first == "hashes" ||
|
|
m.first == "unsigned" ||
|
|
m.first == "age_ts" ||
|
|
m.first == "outlier" ||
|
|
m.first == "destinations")
|
|
continue;
|
|
|
|
member.at(i++) = m;
|
|
}
|
|
|
|
mutable_buffer buf{buf_};
|
|
const string_view ret
|
|
{
|
|
json::stringify(buf, member.data(), member.data() + i)
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
catch(const std::out_of_range &e)
|
|
{
|
|
throw m::BAD_JSON
|
|
{
|
|
"Object has more than %zu member properties.",
|
|
json::iov::max_size
|
|
};
|
|
}
|
|
|
|
bool
|
|
ircd::m::before(const event &a,
|
|
const event &b)
|
|
{
|
|
const event::prev prev{b};
|
|
return prev.prev_events_has(a.event_id);
|
|
}
|
|
|
|
size_t
|
|
ircd::m::degree(const event &event)
|
|
{
|
|
return degree(event::prev{event});
|
|
}
|
|
|
|
size_t
|
|
ircd::m::degree(const event::prev &prev)
|
|
{
|
|
size_t ret{0};
|
|
json::for_each(prev, [&ret]
|
|
(const auto &, const json::array &prevs)
|
|
{
|
|
ret += prevs.count();
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
ircd::m::operator>=(const event &a, const event &b)
|
|
{
|
|
const int room_id_cmp
|
|
{
|
|
cmp(json::get<"room_id"_>(a), json::get<"room_id"_>(b))
|
|
};
|
|
|
|
const auto depth_gte
|
|
{
|
|
json::get<"depth"_>(a) >= json::get<"depth"_>(b)
|
|
};
|
|
|
|
return room_id_cmp > 0 || (room_id_cmp == 0 && depth_gte);
|
|
}
|
|
|
|
bool
|
|
ircd::m::operator<=(const event &a, const event &b)
|
|
{
|
|
const int room_id_cmp
|
|
{
|
|
cmp(json::get<"room_id"_>(a), json::get<"room_id"_>(b))
|
|
};
|
|
|
|
const auto depth_lte
|
|
{
|
|
json::get<"depth"_>(a) <= json::get<"depth"_>(b)
|
|
};
|
|
|
|
return room_id_cmp < 0 || (room_id_cmp == 0 && depth_lte);
|
|
}
|
|
|
|
bool
|
|
ircd::m::operator>(const event &a, const event &b)
|
|
{
|
|
const int room_id_cmp
|
|
{
|
|
cmp(json::get<"room_id"_>(a), json::get<"room_id"_>(b))
|
|
};
|
|
|
|
const auto depth_gt
|
|
{
|
|
json::get<"depth"_>(a) > json::get<"depth"_>(b)
|
|
};
|
|
|
|
return room_id_cmp > 0 || (room_id_cmp == 0 && depth_gt);
|
|
}
|
|
|
|
bool
|
|
ircd::m::operator<(const event &a,
|
|
const event &b)
|
|
{
|
|
const int room_id_cmp
|
|
{
|
|
cmp(json::get<"room_id"_>(a), json::get<"room_id"_>(b))
|
|
};
|
|
|
|
const auto depth_lt
|
|
{
|
|
json::get<"depth"_>(a) < json::get<"depth"_>(b)
|
|
};
|
|
|
|
return room_id_cmp < 0 || (room_id_cmp == 0 && depth_lt);
|
|
}
|
|
|
|
bool
|
|
ircd::m::operator==(const event &a, const event &b)
|
|
{
|
|
//assert(json::get<"room_id"_>(a) == json::get<"room_id"_>(b));
|
|
return a.event_id == b.event_id;
|
|
}
|
|
|
|
uint64_t
|
|
ircd::m::exists(const vector_view<const id::event> &event_ids)
|
|
{
|
|
const vector_view<const string_view> key
|
|
{
|
|
static_cast<const string_view *>(event_ids.data()), event_ids.size()
|
|
};
|
|
|
|
auto &column
|
|
{
|
|
dbs::event_idx
|
|
};
|
|
|
|
return db::has(column, key);
|
|
}
|
|
|
|
bool
|
|
ircd::m::bad(const id::event &event_id)
|
|
{
|
|
bool ret {false};
|
|
index(std::nothrow, event_id, [&ret]
|
|
(const event::idx &event_idx)
|
|
{
|
|
ret = event_idx == 0;
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
size_t
|
|
ircd::m::count(const event::prev &prev)
|
|
{
|
|
size_t ret{0};
|
|
m::for_each(prev, [&ret](const event::id &event_id)
|
|
{
|
|
++ret;
|
|
return true;
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
ircd::m::good(const id::event &event_id)
|
|
{
|
|
return bool(event_id) && index(std::nothrow, event_id) != 0;
|
|
}
|
|
|
|
bool
|
|
ircd::m::exists(const id::event &event_id,
|
|
const bool &good)
|
|
{
|
|
return good?
|
|
m::good(event_id):
|
|
m::exists(event_id);
|
|
}
|
|
|
|
bool
|
|
ircd::m::exists(const id::event &event_id)
|
|
{
|
|
auto &column
|
|
{
|
|
dbs::event_idx
|
|
};
|
|
|
|
return bool(event_id) && has(column, event_id);
|
|
}
|
|
|
|
bool
|
|
ircd::m::my(const event &event)
|
|
{
|
|
const auto &origin(json::get<"origin"_>(event));
|
|
const auto &sender(json::get<"sender"_>(event));
|
|
const auto &eid(event.event_id);
|
|
return
|
|
origin?
|
|
my_host(origin):
|
|
sender?
|
|
my_host(user::id(sender).host()):
|
|
eid?
|
|
my(event::id(eid)):
|
|
false;
|
|
}
|
|
|
|
bool
|
|
ircd::m::my(const id::event &event_id)
|
|
{
|
|
return event_id.version() == "1"?
|
|
self::host(event_id.host()):
|
|
event::my(index(event_id));
|
|
}
|
|
|
|
bool
|
|
ircd::m::event::my(const idx &event_idx)
|
|
{
|
|
return m::query(std::nothrow, event_idx, "origin", []
|
|
(const string_view &origin)
|
|
{
|
|
return m::my_host(origin);
|
|
});
|
|
}
|
|
|
|
//
|
|
// event::event
|
|
//
|
|
|
|
ircd::m::event::event(const json::members &members)
|
|
:super_type
|
|
{
|
|
members
|
|
}
|
|
,event_id
|
|
{
|
|
defined(json::get<"event_id"_>(*this))?
|
|
id{json::get<"event_id"_>(*this)}:
|
|
id{},
|
|
}
|
|
{
|
|
}
|
|
|
|
ircd::m::event::event(const json::iov &members)
|
|
:event
|
|
{
|
|
members,
|
|
members.has("event_id")?
|
|
id{members.at("event_id")}:
|
|
id{}
|
|
}
|
|
{
|
|
}
|
|
|
|
ircd::m::event::event(const json::iov &members,
|
|
const id &id)
|
|
:super_type
|
|
{
|
|
members
|
|
}
|
|
,event_id
|
|
{
|
|
id
|
|
}
|
|
{
|
|
}
|
|
|
|
ircd::m::event::event(const json::object &source)
|
|
:super_type
|
|
{
|
|
source
|
|
}
|
|
,event_id
|
|
{
|
|
defined(json::get<"event_id"_>(*this))?
|
|
id{json::get<"event_id"_>(*this)}:
|
|
id{},
|
|
}
|
|
{
|
|
}
|
|
|
|
ircd::m::event::event(const json::object &source,
|
|
const keys &keys)
|
|
:super_type
|
|
{
|
|
source, keys
|
|
}
|
|
,event_id
|
|
{
|
|
defined(json::get<"event_id"_>(*this))?
|
|
id{json::get<"event_id"_>(*this)}:
|
|
id{},
|
|
}
|
|
{
|
|
}
|
|
|
|
ircd::m::event::event(id::buf &buf,
|
|
const json::object &source,
|
|
const string_view &version)
|
|
:event
|
|
{
|
|
source,
|
|
version == "1"?
|
|
id{json::string(source.get("event_id"))}:
|
|
version == "2"?
|
|
id{json::string(source.get("event_id"))}:
|
|
version == "3"?
|
|
id{id::v3{buf, source}}:
|
|
version == "4"?
|
|
id{id::v4{buf, source}}:
|
|
source.has("event_id")?
|
|
id{json::string(source.at("event_id"))}:
|
|
id{id::v4{buf, source}},
|
|
}
|
|
{
|
|
}
|
|
|
|
ircd::m::event::event(const json::object &source,
|
|
const id &event_id)
|
|
try
|
|
:super_type
|
|
{
|
|
source
|
|
}
|
|
,event_id
|
|
{
|
|
event_id?
|
|
event_id:
|
|
defined(json::get<"event_id"_>(*this))?
|
|
id{json::get<"event_id"_>(*this)}:
|
|
id{},
|
|
}
|
|
{
|
|
}
|
|
catch(const json::parse_error &e)
|
|
{
|
|
log::error
|
|
{
|
|
log, "Event %s from JSON source (%zu bytes) :%s",
|
|
event_id?
|
|
string_view{event_id}:
|
|
"<event_id in source>"_sv,
|
|
string_view{source}.size(),
|
|
e.what(),
|
|
};
|
|
}
|
|
|
|
ircd::m::event::event(const json::object &source,
|
|
const id &event_id,
|
|
const keys &keys)
|
|
try
|
|
:super_type
|
|
{
|
|
source, keys
|
|
}
|
|
,event_id
|
|
{
|
|
event_id?
|
|
event_id:
|
|
defined(json::get<"event_id"_>(*this))?
|
|
id{json::get<"event_id"_>(*this)}:
|
|
id{},
|
|
}
|
|
{
|
|
}
|
|
catch(const json::parse_error &e)
|
|
{
|
|
log::error
|
|
{
|
|
log, "Event %s from JSON source (%zu bytes) keys:%zu :%s",
|
|
string_view{event_id},
|
|
string_view{source}.size(),
|
|
keys.count(),
|
|
e.what(),
|
|
};
|
|
}
|