📒 EOS - Журнал разработчика. Звездная дата 201707.7 (@dan)
На этой неделе мы добились больших успехов в совершенствовании архитектуры EOS и определении API для разработчиков. В частности, мы подобрали модель программирования, которая должна обеспечивать параллелизм при максимальной простоте использования и ясности кода.
Обновленные примеры контрактов
Те, кто следил за нашим github, возможно, заметили, что мы начали интегрировать ряд примеров контрактов, чтобы протестировать нашу архитектуру и убедиться в том, что мы можем покрыть все желаемые варианты использования.
Вот как простой валютный контракт выглядит сегодня:
/**
* Transfer requires that the sender and receiver be the first two
* accounts notified and that the sender has provided authorization.
*/
struct Transfer {
AccountName from;
AccountName to;
uint64_t amount = 0;
char memo[]; /// extra bytes are treated as a memo and ignored by logic
};
struct CurrencyAccount {
CurrencyAccount( uint64_t b = 0 ):balance(b){}
uint64_t balance = 0;
/** used as a hint to Db::store to tell it which table name within the current
* scope to use. Data is stored in tables under a structure like
*
* scope/code/table/key->value
*
* In this case the "singleton" table is designed for constant named keys that
* refer to a unique object type. User account balances are stored here:
*
* username/currency/singleton/account -> CurrencyAccount
*/
static Name tableId() { return NAME("singleton"); }
};
void apply_currency_transfer() {
const auto& transfer = currentMessage<Transfer>();
/** will call apply_currency_transfer() method in code defined by transfer.to and transfer.from */
requireNotice( transfer.to, transfer.from );
requireAuth( transfer.from );
static CurrencyAccount from_account;
static CurrencyAccount to_account;
Db::get( transfer.from, NAME("account"), from_account );
Db::get( transfer.to, NAME("account"), to_account );
assert( from_account.balance >= transfer.amount, "insufficient funds" );
from_account.balance -= transfer.amount;
to_account.balance += transfer.amount;
if( from_account.balance == 0 )
Db::remove<CurrencyAccount>( transfer.from, NAME("account") );
else
Db::store( transfer.from, NAME("account"), from_account );
Db::store( transfer.to, NAME("account"), to_account );
}
У этого валютного контракта есть несколько примечательных аспектов:
- Он выглядит как обычный последовательный код
- Но он может совершать переводы между двумя парами пользователей параллельно
Что это значит? Это значит, что пока Алиса переводит средства Бобу, Сэм может переводить их Джилл. Выполнение этого валютного контракта больше не ограничено однопоточной производительностью предыдущих валютных контрактов.
Начальный вариант биржевого контракта
Мы также внедрили простой биржевой контракт, который принимает депозиты и выводы средств от двух разных валютных контрактов:
struct Account {
uint64_t a = 0;
uint64_t b = 0;
int open_orders = 0;
bool isEmpty()const { return !(a|b|open_orders); }
/**
* Balance records for all exchange users are stored here
* exchange/exchange/balance/username -> Balance
*/
static Name tableId() { return Name("balance"); }
};
/**
* This method is called after the "transfer" action of code
* "currencya" is called and "exchange" is listed in the notifiers.
*/
void apply_currencya_transfer() {
const auto& transfer = currentMessage<Transfer>();
if( transfer.to == "exchange" ) {
static Balance to_balance;
Db::get( transfer.from, to_balance );
to_balance.a += transfer.amount;
Db::store( transfer.from, to_balance );
} else if( transfer.from == "exchange" ) {
requireAuth( transfer.to ); /// require the reciever of funds (account owner) to authorize this transfer
static Balance to_balance;
auto balance = Db::get( transfer.to, to_balance );
assert( balance.a >= transfer.amount, "insufficient funds to withdraw" );
balance.a -= transfer.amount;
if( balance.isEmpty() )
Db::remove<Balance>( transfer.to );
else
Db::store( transfer.to, to_balance );
} else {
assert( false, "notified on transfer that is not relevant to this exchange" );
}
}
/**
* This method is called after the "transfer" action of code
* "currencya" is called and "exchange" is listed in the notifiers.
*/
void apply_currencyb_transfer() {
const auto& transfer = currentMessage<Transfer>();
if( transfer.to == "exchange" ) {
static Balance to_balance;
Db::get( transfer.from, to_balance );
to_balance.b += transfer.amount;
Db::store( transfer.from, to_balance );
} else if( transfer.from == "exchange" ) {
requireAuth( transfer.to ); /// require the reciever of funds (account owner) to authorize this transfer
static Balance to_balance;
auto balance = Db::get( transfer.to, to_balance );
assert( balance.b >= transfer.amount, "insufficient funds to withdraw" );
balance.b -= transfer.amount;
if( balance.isEmpty() )
Db::remove<Balance>( transfer.to );
else
Db::store( transfer.to, to_balance );
} else {
assert( false, "notified on transfer that is not relevant to this exchange" );
}
}
Что интересно в этой реализации, так это то, что депозиты и выводы происходят без каких-либо асинхронных обратных вызовов или других сложных ожидающих состояний. Вместо того, чтобы пользователь просил биржу приказать валютному контракту снять средства, биржа дает каждому разрешение на перевод средств с биржи с предостережением о том, что обработчик биржи вызывается как для депозитов, так и для выводов. Если пользователь с помощью биржевого контракта пытается вывести больше средств, чем есть на его балансе, обработчик отклонит попытку перевода.
Мы еще не добавили рабочие тесты биржевого контракта для депозита и вывода, но это стоит на повестке дня на следующей неделе. Мы также будем внедрять базовое соцмедиа приложение, чтобы доказать, что мы покрываем все существующие варианты использования.
Синхронные вызовы
Одна из самых больших разработок этой недели - это архитектура, которая позволяет производить синхронные вызовы между тесно связанными приложениями, сохраняя при этом большую часть преимуществ параллелизма. В нашем блоге будет более подробно описано устройство памяти и структура параллельного выполнения.
Стоимость хранения
Некоторые люди выразили озабоченность тем, что если в какой-то момент токены будут стоить 30 миллиардов долларов, а емкость хранилища составит всего 1 ТБ, то стоимость хранения будет слишком высокой. Я подготовил статью, в которой объясняется, как мы это решаем и сохраняем стоимость хранения по большей части независимой от цены токена.
Разработка EOS.IO продвигается успешно!
Свежие новости в Телеграм: t.me/EOS_RU
Оригинал поста: ЗДЕСЬ