ݺߣ

ݺߣShare a Scribd company logo
Методики построения и архитектура
надежных смарт-контрактов
Александр Мазалецкий,
Dicoiner, inc
Рассмотрим на примере ERC-20 токенов и ICO
• Стандарт ERC-20: разработан в конце 2015 года, де-факто стандарт
для финансовых активов
• Многие предприятия решили собрать инвестиции посредством Initial
Coin Offering (ICO)
• In 2016, 64 ICOs собрали более 103 млн $ (CoinDesk)
Рассмотрим, например ERC-20 стандарт
Функция Описание
totalSupply() Общение количество токенов в обращении
balanceOf(A) Количество токенов принадлежащих A
transfer(A,x) Отправить количество токенов x стороне A
transferFrom(A,x) Списать количество токенов x со стороны A
approve(A,x) Подтвердить, что сторона A может списать токены
x от моего имени
allowance(A,B) Количество токенов, разрешенных стороне B на
вывод от лица стороны A
Безопасность
• Безопасность процесса ICO и надежность
контрактов является жизненно-важной
проблемой
• Угрозы:
• Потеря токенов из-за неправильного
поведения контракта
• Потеря доступности, другие контракты не
мог взаимодействовать с токеном из-за
ошибок
• Типовые уязвимости
• Небезопасный код
• Интерфейс с ошибками
Кейс1: Реализация смарт-контракта
Исключения в Solidity
• C1.F () не может перехватывать исключения
• C1.call () не может получить возвращаемые значения
• Исключения должны быть для исключительных событий!
function F(uint x)
returns uint{
If(x>42) throw;
else return 3}
X=C1.F(10);
….
X = C1.call(…”F”…)
C2 C1
Исключения вместо возвращаемого значения функции
Пример кода:
function transferFrom(address _from, address _to, uint _value) returns (bool
success) {

var _allowance = allowed[_from][msg.sender];



balances[_to] = safeAdd(balances[_to], _value);

balances[_from] = safeSub(balances[_from], _value); 1

allowed[_from][msg.sender] = safeSub(_allowance, _value);

Transfer(_from, _to, _value);

return true;

}
function safeSub(uint a, uint b) internal returns (uint) {

assert(b <= a);

return a - b;

}

Исключения вместо возвращаемого значения функции
• Пример кода:
• function transferFrom(address _from, address _to, uint _value) returns (bool
success) {

var _allowance = allowed[_from][msg.sender];



balances[_to] = safeAdd(balances[_to], _value);

balances[_from] = safeSub(balances[_from], _value); 

allowed[_from][msg.sender] = safeSub(_allowance, _value);

Transfer(_from, _to, _value);

return true;

}
• Если баланса _from (отправителя) недостаточно или слишком маленькое значение
для допустимых токенов, генерируется исключение (а должно быть false).

Опираясь на выше сказанное
• function transferFrom(address _from, address _to, uint _value)
returns (bool success) {

var _allowance = allowed[_from][msg.sender];

balances[_to] = safeAdd(balances[_to], _value);

balances[_from] = safeSub(balances[_from], _value);

allowed[_from][msg.sender] = safeSub(_allowance, _value);

Transfer(_from, _to, _value);

return true;

}
• Значение баланса отправителя_to всегда будет расти
• Необходимо, предусмотреть, чтобы в случае понижения значение
allowance возвращалось исключение 

Переполнение в SafeMath
• function safeMul(uint a, uint b) internal returns (uint) {

uint c = a * b;

assert(a == 0 || c / a == b);

return c;

}

• Код полагается на недокументированное поведение в Solidity
• Сначала идет создание переполнения, а зачем попытка обнаружить
• Переполнение может вести себя по разному, в итоге может привести к полной потере
токенов
• И все теже проблемы что были с SafeAdd
Кейс 2: the Approve() функция
Ломаем ERC-20 standard
Ситуация
1. Алиса разрешает перевести Бобу N-токенов
2. Затем Алиса решила изменить количество доступных токенов Бобу
для перевода и разрешает перевести M-токенов
3. Боб замечает последнюю транзакцию, до того как она была
подтверждена, и отправляет их Кэрол
4. После того как все транзакции Алиса подтверждены, Боб отправляет
Алиса еще N-токенов, в итоге переводит Кэрол, M+N токенов
5. Действия Боба не регистрируются нужным образом, событие transfer
знает только адреса Алиса и Кэрол
6. И еще больше проблем будет, если Алиса разрешила и Кэрол
переводить некоторое количество токенов
Предложение: генерация новых событий
1) function approve( address _spender, uint256
_currentValue, uint256 _value) returns (bool success)
Если значение разрешенное для _spender равно _currentValue, то
переписываем _value и возвращаем значение true, иначе false.
2) event Transfer( address indexed _spender, address
indexed _from, address indexed _to, uint256 _value)
3) event Approval( address indexed _owner, address
indexed _spender, uint256 _oldValue, uint256 _value)
Другие предложения
Четкие требования и понимание поведения
• Определите функциональные требования для контракта и API
• Описывайте все возможные модели поведения!!!
• Следуйте четко базовым принципам безопасности
Разрабатывайте тесты
Полное следование стандартам
function transferFrom (address _from, address _to, uint256 _value)
returns (bool success) {
if (allowances [_from][msg.sender] < _value) return false;
if (accounts [_from] < _value) return false;
allowances [_from][msg.sender] =
safeSub (allowances [_from][msg.sender], _value);
if (_value > 0 && _from != _to) {
accounts [_from] = safeSub (accounts [_from], _value);
accounts [_to] = safeAdd (accounts [_to], _value);
Transfer (_from, _to, _value);
}
return true;
}
Спасибо за внимание
Есть вопросы?
Пишите, t.me/dicoiner

More Related Content

Надежные смарт контракты #spblockchain

  • 1. Методики построения и архитектура надежных смарт-контрактов Александр Мазалецкий, Dicoiner, inc
  • 2. Рассмотрим на примере ERC-20 токенов и ICO • Стандарт ERC-20: разработан в конце 2015 года, де-факто стандарт для финансовых активов • Многие предприятия решили собрать инвестиции посредством Initial Coin Offering (ICO) • In 2016, 64 ICOs собрали более 103 млн $ (CoinDesk)
  • 3. Рассмотрим, например ERC-20 стандарт Функция Описание totalSupply() Общение количество токенов в обращении balanceOf(A) Количество токенов принадлежащих A transfer(A,x) Отправить количество токенов x стороне A transferFrom(A,x) Списать количество токенов x со стороны A approve(A,x) Подтвердить, что сторона A может списать токены x от моего имени allowance(A,B) Количество токенов, разрешенных стороне B на вывод от лица стороны A
  • 4. Безопасность • Безопасность процесса ICO и надежность контрактов является жизненно-важной проблемой • Угрозы: • Потеря токенов из-за неправильного поведения контракта • Потеря доступности, другие контракты не мог взаимодействовать с токеном из-за ошибок • Типовые уязвимости • Небезопасный код • Интерфейс с ошибками
  • 6. Исключения в Solidity • C1.F () не может перехватывать исключения • C1.call () не может получить возвращаемые значения • Исключения должны быть для исключительных событий! function F(uint x) returns uint{ If(x>42) throw; else return 3} X=C1.F(10); …. X = C1.call(…”F”…) C2 C1
  • 7. Исключения вместо возвращаемого значения функции Пример кода: function transferFrom(address _from, address _to, uint _value) returns (bool success) {
 var _allowance = allowed[_from][msg.sender];
 
 balances[_to] = safeAdd(balances[_to], _value);
 balances[_from] = safeSub(balances[_from], _value); 1
 allowed[_from][msg.sender] = safeSub(_allowance, _value);
 Transfer(_from, _to, _value);
 return true;
 } function safeSub(uint a, uint b) internal returns (uint) {
 assert(b <= a);
 return a - b;
 }

  • 8. Исключения вместо возвращаемого значения функции • Пример кода: • function transferFrom(address _from, address _to, uint _value) returns (bool success) {
 var _allowance = allowed[_from][msg.sender];
 
 balances[_to] = safeAdd(balances[_to], _value);
 balances[_from] = safeSub(balances[_from], _value); 
 allowed[_from][msg.sender] = safeSub(_allowance, _value);
 Transfer(_from, _to, _value);
 return true;
 } • Если баланса _from (отправителя) недостаточно или слишком маленькое значение для допустимых токенов, генерируется исключение (а должно быть false).

  • 9. Опираясь на выше сказанное • function transferFrom(address _from, address _to, uint _value) returns (bool success) {
 var _allowance = allowed[_from][msg.sender];
 balances[_to] = safeAdd(balances[_to], _value);
 balances[_from] = safeSub(balances[_from], _value);
 allowed[_from][msg.sender] = safeSub(_allowance, _value);
 Transfer(_from, _to, _value);
 return true;
 } • Значение баланса отправителя_to всегда будет расти • Необходимо, предусмотреть, чтобы в случае понижения значение allowance возвращалось исключение 

  • 10. Переполнение в SafeMath • function safeMul(uint a, uint b) internal returns (uint) {
 uint c = a * b;
 assert(a == 0 || c / a == b);
 return c;
 }
 • Код полагается на недокументированное поведение в Solidity • Сначала идет создание переполнения, а зачем попытка обнаружить • Переполнение может вести себя по разному, в итоге может привести к полной потере токенов • И все теже проблемы что были с SafeAdd
  • 11. Кейс 2: the Approve() функция Ломаем ERC-20 standard
  • 12. Ситуация 1. Алиса разрешает перевести Бобу N-токенов 2. Затем Алиса решила изменить количество доступных токенов Бобу для перевода и разрешает перевести M-токенов 3. Боб замечает последнюю транзакцию, до того как она была подтверждена, и отправляет их Кэрол 4. После того как все транзакции Алиса подтверждены, Боб отправляет Алиса еще N-токенов, в итоге переводит Кэрол, M+N токенов 5. Действия Боба не регистрируются нужным образом, событие transfer знает только адреса Алиса и Кэрол 6. И еще больше проблем будет, если Алиса разрешила и Кэрол переводить некоторое количество токенов
  • 13. Предложение: генерация новых событий 1) function approve( address _spender, uint256 _currentValue, uint256 _value) returns (bool success) Если значение разрешенное для _spender равно _currentValue, то переписываем _value и возвращаем значение true, иначе false. 2) event Transfer( address indexed _spender, address indexed _from, address indexed _to, uint256 _value) 3) event Approval( address indexed _owner, address indexed _spender, uint256 _oldValue, uint256 _value)
  • 15. Четкие требования и понимание поведения • Определите функциональные требования для контракта и API • Описывайте все возможные модели поведения!!! • Следуйте четко базовым принципам безопасности
  • 17. Полное следование стандартам function transferFrom (address _from, address _to, uint256 _value) returns (bool success) { if (allowances [_from][msg.sender] < _value) return false; if (accounts [_from] < _value) return false; allowances [_from][msg.sender] = safeSub (allowances [_from][msg.sender], _value); if (_value > 0 && _from != _to) { accounts [_from] = safeSub (accounts [_from], _value); accounts [_to] = safeAdd (accounts [_to], _value); Transfer (_from, _to, _value); } return true; }
  • 18. Спасибо за внимание Есть вопросы? Пишите, t.me/dicoiner