9#include "crypto/sha256.h" 
   18#include <evmc/hex.hpp> 
   19#include <evmone/evmone.h> 
   25                       evmc_tx_context tx_context,
 
   29        : m_log(std::move(log)),
 
   30          m_try_lock_callback(std::move(try_lock_callback)),
 
   31          m_tx_context(tx_context),
 
   33          m_is_readonly_run(is_readonly_run),
 
   34          m_ticket_number(ticket_number) {
 
   35        m_receipt.
m_tx = m_tx;
 
 
   39    auto evm_host::get_account(
const evmc::address& addr, 
bool write) 
const 
   40        -> std::optional<evm_account> {
 
   42                     "EVM request account:",
 
   47        if(is_precompile(addr)) {
 
   49            m_accessed_addresses.insert(addr);
 
   53        auto it = m_accounts.find(addr);
 
   54        if(it != m_accounts.end() && (it->second.second || !write)) {
 
   55            return it->second.first;
 
   60            "EVM request account not in cache or wrong lock, getting [",
 
   65        auto maybe_v = get_key(addr_key, write);
 
   66        if(!maybe_v.has_value()) {
 
   70        m_accessed_addresses.insert(addr);
 
   71        auto& v = maybe_v.value();
 
   73            m_accounts[addr] = {std::nullopt, write};
 
   77        assert(maybe_acc.has_value());
 
   78        auto& acc = maybe_acc.value();
 
   79        m_accounts[addr] = {acc, 
write};
 
   85        const auto* log_str = 
"evm_account_exists";
 
   86        m_log->trace(log_str, 
to_hex(addr));
 
   87        auto maybe_acc = get_account(addr, 
false);
 
   88        if(!maybe_acc.has_value()) {
 
   91        auto& acc = maybe_acc.value();
 
   92        return !acc.m_destruct;
 
 
   96                               const evmc::bytes32& key) 
const noexcept 
   98        const auto* log_str = 
"evm_get_storage";
 
  100        auto maybe_storage = get_account_storage(addr, key, 
false);
 
  101        return maybe_storage.value_or(evmc::bytes32{});
 
 
  105                               const evmc::bytes32& key,
 
  106                               const evmc::bytes32& value) 
noexcept 
  107        -> evmc_storage_status {
 
  108        const auto* log_str = 
"evm_set_storage";
 
  111        auto ret_val = std::optional<evmc_storage_status>();
 
  112        auto maybe_acc = get_account(addr, 
false);
 
  113        if(!maybe_acc.has_value()) {
 
  114            if(!m_is_readonly_run) {
 
  115                maybe_acc = get_account(addr, 
true);
 
  116                assert(!maybe_acc.has_value());
 
  119            ret_val = EVMC_STORAGE_ADDED;
 
  120            maybe_acc.value().m_modified.insert(key);
 
  121            m_accounts[addr] = {maybe_acc.value(), !m_is_readonly_run};
 
  123        auto& acc = maybe_acc.value();
 
  126            = get_account_storage(addr, key, !m_is_readonly_run);
 
  127        auto prev_value = maybe_storage.value_or(evmc::bytes32{});
 
  129        auto modified = acc.m_modified.find(key) != acc.m_modified.end();
 
  130        if(!ret_val.has_value()) {
 
  131            if(prev_value == value || modified) {
 
  136                ret_val = EVMC_STORAGE_ASSIGNED;
 
  137            } 
else if(evmc::is_zero(value) && !modified) {
 
  138                ret_val = EVMC_STORAGE_DELETED;
 
  140                ret_val = EVMC_STORAGE_MODIFIED;
 
  141                acc.m_modified.insert(key);
 
  144        m_account_storage[addr][key] = {value, !m_is_readonly_run};
 
  145        m_accounts[addr].first = acc;
 
  146        assert(ret_val.has_value());
 
  147        return ret_val.value();
 
 
  152        const auto* log_str = 
"evm_get_balance";
 
  153        m_log->trace(log_str, 
to_hex(addr));
 
  154        auto maybe_acc = get_account(addr, 
false);
 
  155        if(!maybe_acc.has_value()) {
 
  158        auto& acc = maybe_acc.value();
 
  159        return acc.m_balance;
 
 
  164        const auto* log_str = 
"evm_get_code_size";
 
  165        m_log->trace(log_str, 
to_hex(addr));
 
  166        if(is_precompile(addr)) {
 
  171        auto maybe_code = get_account_code(addr, 
false);
 
 
  177        const auto* log_str = 
"evm_get_code_hash";
 
  178        m_log->trace(log_str, 
to_hex(addr));
 
  180        auto maybe_code = get_account_code(addr, 
false);
 
  181        if(!maybe_code.has_value()) {
 
  184        auto& code = maybe_code.value();
 
  185        auto sha = CSHA256();
 
  186        sha.Write(code.data(), code.size());
 
  187        auto ret = evmc::bytes32();
 
  188        sha.Finalize(&ret.bytes[0]);
 
 
  194                             uint8_t* buffer_data,
 
  195                             size_t buffer_size) 
const noexcept -> 
size_t {
 
  196        const auto* log_str = 
"evm_copy_code";
 
  197        m_log->trace(log_str, 
to_hex(addr), code_offset);
 
  199        auto maybe_code = get_account_code(addr, 
false);
 
  200        if(!maybe_code.has_value()) {
 
  204        const auto& code = maybe_code.value();
 
  206        if(code_offset >= code.size()) {
 
  210        const auto n = std::min(buffer_size, code.size() - code_offset);
 
  212            std::copy_n(&code[code_offset], n, buffer_data);
 
 
  218                                const evmc::address& beneficiary) 
noexcept 
  220        m_log->trace(
"EVM selfdestruct:", 
to_hex(addr), 
to_hex(beneficiary));
 
  222        transfer(addr, beneficiary, evmc::uint256be{});
 
 
  226    auto evm_host::create(
const evmc_message& msg) 
noexcept -> evmc::Result {
 
  227        auto maybe_sender_acc = get_account(msg.sender, 
false);
 
  228        if(!maybe_sender_acc.has_value()) {
 
  229            m_log->warn(
"EVM CREATE: sender account not found");
 
  231                evmc::make_result(evmc_status_code::EVMC_REVERT,
 
  237        auto& sender_acc = maybe_sender_acc.value();
 
  239        auto new_addr = evmc::address();
 
  240        if(msg.kind == EVMC_CREATE) {
 
  251        if(!evmc::is_zero(msg.value)) {
 
  252            transfer(msg.sender, new_addr, msg.value);
 
  256            m_receipt.m_create_address = new_addr;
 
  259        auto call_msg = evmc_message();
 
  260        call_msg.depth = msg.depth;
 
  261        call_msg.sender = msg.sender;
 
  262        call_msg.value = msg.value;
 
  263        call_msg.recipient = new_addr;
 
  264        call_msg.kind = EVMC_CALL;
 
  267        call_msg.gas = msg.gas;
 
  269        auto res = execute(call_msg, msg.input_data, msg.input_size);
 
  271        if(res.status_code == EVMC_SUCCESS) {
 
  277            res.create_address = new_addr;
 
  279            auto maybe_acc = get_account(new_addr, !m_is_readonly_run);
 
  280            if(!maybe_acc.has_value()) {
 
  281                maybe_acc = evm_account();
 
  283            auto& acc = maybe_acc.value();
 
  284            m_accounts[new_addr] = {acc, !m_is_readonly_run};
 
  286            auto maybe_code = get_account_code(new_addr, !m_is_readonly_run);
 
  287            if(!maybe_code.has_value()) {
 
  290            auto& code = maybe_code.value();
 
  291            code.resize(res.output_size);
 
  292            std::memcpy(code.data(), res.output_data, res.output_size);
 
  293            m_account_code[new_addr] = {code, !m_is_readonly_run};
 
  297            m_receipt.m_output_data.resize(res.output_size);
 
  298            std::memcpy(m_receipt.m_output_data.data(),
 
  302                = res.status_code == evmc_status_code::EVMC_SUCCESS;
 
  309        if(msg.kind == EVMC_CREATE2 || msg.kind == EVMC_CREATE) {
 
  314        bool is_native_value_transfer{
false};
 
  315        if(!evmc::is_zero(msg.value) && msg.kind == EVMC_CALL) {
 
  316            is_native_value_transfer = 
true;
 
  319            transfer(msg.sender, msg.recipient, msg.value);
 
  323            = msg.kind == EVMC_DELEGATECALL || msg.kind == EVMC_CALLCODE
 
  327        const auto code_size = get_code_size(code_addr);
 
  330            const auto gas_refund = 0;
 
  331            auto res = evmc::make_result(evmc_status_code::EVMC_SUCCESS,
 
  339            if(is_native_value_transfer) {
 
  340                m_receipt.m_success = 
true;
 
  343            return evmc::Result(res);
 
  346        auto code_buf = std::vector<uint8_t>(code_size);
 
  347        [[maybe_unused]] 
auto n
 
  348            = copy_code(code_addr, 0, code_buf.data(), code_buf.size());
 
  349        assert(n == code_size);
 
  352        inp.append(msg.input_data, msg.input_size);
 
  353        m_log->trace(
"EVM call:",
 
  360        auto res = execute(msg, code_buf.data(), code_buf.size());
 
  364            m_receipt.m_output_data.resize(res.output_size);
 
  365            std::memcpy(m_receipt.m_output_data.data(),
 
  369                = res.status_code == evmc_status_code::EVMC_SUCCESS;
 
 
  387        const evmc::address& addr,
 
  391        const evmc::bytes32 topics[],
 
  392        size_t topics_count) 
noexcept {
 
  395        l.m_data.resize(data_size);
 
  396        std::memcpy(l.m_data.data(), data, data_size);
 
  397        for(
size_t i = 0; i < topics_count; i++) {
 
  399            l.m_topics.push_back(topics[i]);
 
  401        m_receipt.m_logs.push_back(l);
 
 
  405        -> evmc_access_status {
 
  406        m_log->trace(
"EVM access_account:", 
to_hex(addr));
 
  407        if(m_accessed_addresses.find(addr) != m_accessed_addresses.end()) {
 
  408            return EVMC_ACCESS_WARM;
 
  410        m_accessed_addresses.insert(addr);
 
  411        return EVMC_ACCESS_COLD;
 
 
  415                                  const evmc::bytes32& key) 
noexcept 
  416        -> evmc_access_status {
 
  417        m_log->trace(
"EVM access_storage:", 
to_hex(addr), 
to_hex(key));
 
  418        auto elem = std::make_pair(addr, key);
 
  419        if(m_accessed_storage_keys.find(elem)
 
  420           != m_accessed_storage_keys.end()) {
 
  421            return EVMC_ACCESS_WARM;
 
  423        m_accessed_storage_keys.insert(elem);
 
  424        return EVMC_ACCESS_COLD;
 
 
  428        std::optional<interface::ticket_number_type> tn) 
const 
  431            tn = m_ticket_number;
 
  437        sha.Write(tn_buf.c_ptr(), tn_buf.size());
 
  438        sha.Finalize(tn_hash.data());
 
 
  445        std::optional<interface::ticket_number_type> tn) 
const 
  448            tn = m_ticket_number;
 
  453        sha.Write(addr.bytes, 
sizeof(addr.bytes));
 
  454        sha.Write(tn_buf.c_ptr(), tn_buf.size());
 
  455        sha.Finalize(log_index_hash.data());
 
 
  461        auto logs = get_sorted_logs();
 
  462        auto keys = std::vector<cbdc::buffer>();
 
  463        for(
auto& log : logs) {
 
 
  469    auto evm_host::get_sorted_logs() const
 
  470        -> std::unordered_map<evmc::address, std::vector<
evm_log>> {
 
  471        auto ret = std::unordered_map<evmc::address, std::vector<evm_log>>();
 
  472        for(
const auto& log : m_receipt.
m_logs) {
 
  473            if(ret.find(log.m_addr) == ret.end()) {
 
  474                ret.insert({log.m_addr, {}});
 
  476            ret[log.m_addr].push_back(log);
 
  482        -> runtime_locking_shard::state_update_type {
 
  484        for(
auto& [addr, acc_data] : m_accounts) {
 
  485            auto& [acc, write] = acc_data;
 
  486            if(!acc.has_value() || !write) {
 
  491            if(!acc->m_destruct) {
 
  497        for(
auto& [addr, acc_code] : m_account_code) {
 
  498            auto& [code, write] = acc_code;
 
  499            if(!code.has_value() || !write) {
 
  507        for(
auto& [addr, acc_storage] : m_account_storage) {
 
  508            for(
auto& [k, elem] : acc_storage) {
 
  509                auto& [value, write] = elem;
 
  510                if(!value.has_value() || !write) {
 
  519        const auto tx = std::make_shared<evm_tx>(m_tx);
 
  520        auto txid = 
tx_id(*tx);
 
  526        auto ordered_logs = get_sorted_logs();
 
  527        for(
auto& addr_log : ordered_logs) {
 
  529            log_idx.m_ticket_number = m_ticket_number;
 
  530            log_idx.m_txid = txid;
 
  531            log_idx.m_logs = addr_log.second;
 
 
  543        m_accounts.insert({addr, {acc, !m_is_readonly_run}});
 
  544        m_accessed_addresses.insert(addr);
 
  545        m_init_state = m_accounts;
 
 
  548    void evm_host::transfer(
const evmc::address& from,
 
  549                            const evmc::address& to,
 
  550                            const evmc::uint256be& value) {
 
  551        const auto* log_str = 
"evm_transfer";
 
  553        auto maybe_acc = get_account(from, !m_is_readonly_run);
 
  554        assert(maybe_acc.has_value());
 
  555        auto& acc = maybe_acc.value();
 
  557        if(evmc::is_zero(value)) {
 
  561            acc.m_destruct = 
true;
 
  563        acc.m_balance = acc.m_balance - val;
 
  564        m_accounts[from] = {acc, !m_is_readonly_run};
 
  566        auto maybe_to_acc = get_account(to, !m_is_readonly_run);
 
  567        if(!maybe_to_acc.has_value()) {
 
  569            maybe_to_acc = evm_account();
 
  571        auto& to_acc = maybe_to_acc.value();
 
  572        to_acc.m_balance = to_acc.m_balance + val;
 
  573        m_accounts[to] = {to_acc, !m_is_readonly_run};
 
  577        if(!m_is_readonly_run) {
 
  578            auto maybe_acc = get_account(m_tx_context.tx_origin, 
true);
 
  579            assert(maybe_acc.has_value());
 
  580            auto& acc = maybe_acc.value();
 
  581            auto gas_refund = evmc::uint256be(
static_cast<uint64_t
>(gas_left))
 
  582                            * m_tx_context.tx_gas_price;
 
  583            acc.m_balance = acc.m_balance + gas_refund;
 
  584            m_accounts[m_tx_context.tx_origin] = {acc, 
true};
 
  587            = evmc::uint256be(
static_cast<uint64_t
>(gas_used));
 
  589            std::chrono::duration_cast<std::chrono::seconds>(
 
  590                std::chrono::system_clock::now().time_since_epoch())
 
 
  595        m_accounts = m_init_state;
 
 
  598    auto evm_host::is_precompile(
const evmc::address& addr) -> 
bool {
 
  599        auto addr_copy = addr;
 
  600        constexpr auto precompile_suffix_sz = 
sizeof(uint16_t);
 
  602            &addr_copy.bytes[
sizeof(addr_copy.bytes) - precompile_suffix_sz],
 
  604            precompile_suffix_sz);
 
  605        return evmc::is_zero(addr_copy)
 
  606            && addr.bytes[
sizeof(addr_copy.bytes) - 1] != 0;
 
  609    auto evm_host::get_account_storage(
const evmc::address& addr,
 
  610                                       const evmc::bytes32& key,
 
  612        -> std::optional<evmc::bytes32> {
 
  613        m_log->trace(
"EVM request account storage:",
 
  617        if(is_precompile(addr)) {
 
  619            m_accessed_addresses.insert(addr);
 
  623        auto it = m_account_storage.find(addr);
 
  624        if(it != m_account_storage.end()) {
 
  625            auto& m = it->second;
 
  626            auto itt = m.find(key);
 
  627            if(itt != m.end() && (itt->second.second || !write)) {
 
  628                return itt->second.first;
 
  632        auto elem_key = 
make_buffer(storage_key{addr, key});
 
  633        auto maybe_v = get_key(elem_key, write);
 
  634        if(!maybe_v.has_value()) {
 
  638        m_accessed_addresses.insert(addr);
 
  639        auto& v = maybe_v.value();
 
  641            m_account_storage[addr][key] = {std::nullopt, 
write};
 
  645        assert(maybe_data.has_value());
 
  646        auto& data = maybe_data.value();
 
  647        m_account_storage[addr][key] = {data, 
write};
 
  651    auto evm_host::get_account_code(
const evmc::address& addr,
 
  653        -> std::optional<evm_account_code> {
 
  654        m_log->trace(
"EVM request account code:", 
to_hex(addr));
 
  656        if(is_precompile(addr)) {
 
  658            m_accessed_addresses.insert(addr);
 
  662        auto it = m_account_code.find(addr);
 
  663        if(it != m_account_code.end() && (it->second.second || !write)) {
 
  664            return it->second.first;
 
  668        auto maybe_v = get_key(elem_key, write);
 
  669        if(!maybe_v.has_value()) {
 
  673        m_accessed_addresses.insert(addr);
 
  674        auto& v = maybe_v.value();
 
  676            m_account_code[addr] = {std::nullopt, 
write};
 
  680        assert(maybe_code.has_value());
 
  681        auto& code = maybe_code.value();
 
  682        m_account_code[addr] = {code, 
write};
 
  686    auto evm_host::get_key(
const cbdc::buffer& key, 
bool write) 
const 
  687        -> std::optional<broker::value_type> {
 
  688        const auto* log_str = 
"get_key";
 
  690            = std::promise<broker::interface::try_lock_return_type>();
 
  691        auto res_fut = res_prom.get_future();
 
  693        m_log->trace(m_ticket_number, log_str, key.to_hex(), 
"write =", write);
 
  695        auto ret = m_try_lock_callback(
 
  697            write ? broker::lock_type::write : broker::lock_type::read,
 
  699                res_prom.set_value(res);
 
  703            m_log->trace(m_ticket_number,
 
  704                         "failed to make try_lock request, retrying");
 
  709        for(
size_t i = 0;; i++) {
 
  710            auto status = res_fut.wait_for(std::chrono::seconds(1));
 
  711            if(status == std::future_status::ready) {
 
  714            m_log->trace(m_ticket_number,
 
  721        auto res = res_fut.get();
 
  723        m_log->trace(m_ticket_number, 
"got key", key.to_hex());
 
  731                    -> std::optional<cbdc::buffer> {
 
  735                [&](runtime_locking_shard::shard_error& )
 
  736                    -> std::optional<cbdc::buffer> {
 
  744    auto evm_host::execute(
const evmc_message& msg,
 
  746                           size_t code_size) -> evmc::Result {
 
  749            m_vm = std::make_unique<evmc::VM>(evmc_create_evmone());
 
  750            if(!(*m_vm) || !m_vm->is_abi_compatible()) {
 
  751                m_log->error(
"Unable to load EVM implementation");
 
  752                const auto gas_refund = 0;
 
  753                auto res = evmc::make_result(evmc_status_code::EVMC_FAILURE,
 
  758                return evmc::Result(res);
 
  762        auto res = m_vm->execute(*
this,
 
  763                                 EVMC_LATEST_STABLE_REVISION,
 
Buffer to store and retrieve byte data.
 
auto get_log_index_keys() const -> std::vector< cbdc::buffer >
Return the keys of the log indexes - these are sha256(addr, ticket) and will get value 1 - to indicat...
 
auto should_retry() const -> bool
Returns whether the transaction needs to be retried due to a transient error.
 
auto get_balance(const evmc::address &addr) const noexcept -> evmc::uint256be override final
 
auto get_code_hash(const evmc::address &addr) const noexcept -> evmc::bytes32 override final
 
auto get_tx_context() const noexcept -> evmc_tx_context override final
 
void finalize(int64_t gas_left, int64_t gas_used)
Finalizes the state updates resulting from the transaction.
 
void insert_account(const evmc::address &addr, const evm_account &acc)
Inserts an account into the host.
 
void revert()
Set the state updates to revert the transaction changes due to a contract error.
 
auto ticket_number_key(std::optional< interface::ticket_number_type > tn=std::nullopt) const -> cbdc::buffer
Return the key for the host's ticket number, which is the hash of the ticket number's serialized repr...
 
auto get_storage(const evmc::address &addr, const evmc::bytes32 &key) const noexcept -> evmc::bytes32 override final
 
auto account_exists(const evmc::address &addr) const noexcept -> bool override
 
evm_host(std::shared_ptr< logging::log > log, interface::try_lock_callback_type try_lock_callback, evmc_tx_context tx_context, evm_tx tx, bool is_readonly_run, interface::ticket_number_type ticket_number)
Constructs a new host instance.
 
auto copy_code(const evmc::address &addr, size_t code_offset, uint8_t *buffer_data, size_t buffer_size) const noexcept -> size_t override final
 
auto access_storage(const evmc::address &addr, const evmc::bytes32 &key) noexcept -> evmc_access_status override final
 
auto call(const evmc_message &msg) noexcept -> evmc::Result override final
 
auto get_block_hash(int64_t number) const noexcept -> evmc::bytes32 override final
 
void emit_log(const evmc::address &addr, const uint8_t *data, size_t data_size, const evmc::bytes32 topics[], size_t topics_count) noexcept override final
 
auto log_index_key(evmc::address addr, std::optional< interface::ticket_number_type > tn=std::nullopt) const -> cbdc::buffer
Return the key for the indicator of the existence of logs for a particular address at a particular ti...
 
auto set_storage(const evmc::address &addr, const evmc::bytes32 &key, const evmc::bytes32 &value) noexcept -> evmc_storage_status override final
 
auto selfdestruct(const evmc::address &addr, const evmc::address &beneficiary) noexcept -> bool override final
 
auto get_code_size(const evmc::address &addr) const noexcept -> size_t override final
 
auto access_account(const evmc::address &addr) noexcept -> evmc_access_status override final
 
auto get_state_updates() const -> runtime_locking_shard::state_update_type
Return the changes to the state resulting from transaction execution.
 
std::function< bool(broker::key_type, broker::lock_type, broker::interface::try_lock_callback_type)> try_lock_callback_type
Callback function type for acquiring locks during function execution.
 
parsec::ticket_machine::ticket_number_type ticket_number_type
Type alias for a ticket number.
 
std::variant< value_type, error_code, runtime_locking_shard::shard_error > try_lock_return_type
Return type from a try lock operation.
 
error_code
Error codes returned by broker operations.
 
std::vector< uint8_t > evm_account_code
Type alias for EVM account code.
 
auto to_hex(const evmc::address &addr) -> std::string
 
auto contract_address2(const evmc::address &sender, const evmc::bytes32 &salt, const cbdc::hash_t &bytecode_hash) -> evmc::address
Calculates a contract address for the CREATE2 call keccak256(0xFF | sender | salt | keccak256(bytecod...
 
auto contract_address(const evmc::address &sender, const evmc::uint256be &nonce) -> evmc::address
Calculates a contract address for the CREATE call keccak256(rlp([sender,nonce]))
 
auto tx_id(const cbdc::parsec::agent::runner::evm_tx &tx, uint64_t chain_id) -> cbdc::hash_t
Calculate ethereum-compatible txid.
 
runtime_locking_shard::value_type value_type
Shard value type.
 
std:: unordered_map< key_type, value_type, hashing::const_sip_hash< key_type > > state_update_type
Type for state updates to a shard. A map of keys and their new values.
 
@ write
Write lock. Only one ticket can hold this lock at a time.
 
@ transfer
Base token transfer.
 
overloaded(Ts...) -> overloaded< Ts... >
 
auto from_buffer(nuraft::buffer &buf) -> std::optional< T >
Deserialize object of given type from a nuraft::buffer.
 
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
 
@ buffer
A singular RLP value (byte array)
 
auto keccak_data(const void *data, size_t len) -> hash_t
Calculates the Keccak256 hash of the specified data.
 
auto make_buffer(const T &obj) -> std::enable_if_t< std::is_same_v< B, nuraft::ptr< nuraft::buffer > >, nuraft::ptr< nuraft::buffer > >
Serialize object into nuraft::buffer using a cbdc::nuraft_serializer.
 
uint64_t m_timestamp
Timestamp of the transaction - needed to provide a timestamp in pretend blocks.
 
cbdc::parsec::agent::runner::interface::ticket_number_type m_ticket_number
Ticket number that ran this TX - needed to map to pretend blocks.
 
evmc::uint256be m_gas_used
Gas used in transaction.
 
std::vector< evm_log > m_logs
List of logs emitted during transaction.
 
evm_tx m_tx
EVM transaction.
 
Type for account storage keys.