DEV: Рубиновые тона – Telegram
DEV: Рубиновые тона
3.21K subscribers
144 photos
2 videos
8 files
981 links
Анонсы новых видео о программировании (Ruby/Rails, Solidity/Ethereum, Python, JS и не только), практические советы, обзор полезных инструментов и новости из мира IT
Download Telegram
А суть здесь в чём. У этих ребят вышло так, что они неправильно учитывали кол-во ставок сделанных и вместо того, чтобы прибавлять общее кол-во ставок (от 1 до 3) делали всегда ++ то есть инкремент на 1. Это привело к тому, что одно условие всегда оказывалось ложным и деньги никогда нельзя было снять.

Это не говоря о том, что один умник заблочил процесс выплаты refund, тк отправил проигрышную ставку не с кошелька, а с контракта. В контракте у него функция receive, которая всегда возвращает ошибку. В итоге, так как рефанды делаются в цикле, в какой-то момент процесс застопорился из-за этой самой ошибки в receive. Вышло, что ни владельцы деньги снять не могут, ни участники, тк пока не выплачены рефанды, то и средства забрать нельзя https://twitter.com/0xInuarashi/status/1517676162138267648
😁6👍2😢1
Немного подробнее.

Если атакующий делает проигрышную ставку с контракта, а в контракте есть receive, которая всегда возвращает ошибку, то попытки возместить проигравшую ставку заведомо обречены. В коде возврат делался стандартным образом:

            if (refund > 0) {
(bool sent, ) = bidData.bidder.call{value: refund}("");
require(sent, "Failed to refund bidder");
}


А коль скоро возвратов много и их надо обрабатывать в цикле, то как только попадается адрес контракта злоумышленника, то и вся цепочка обрывается. Правда потом злоумышленник всё-таки вышел на связь и сказал, что просто хотел показать ненадёжность аукциона и этот блок убрал (у него в receive просто стояло элементарное условие вида "возвращать ошибку/не возвращать ошибку"). Если ошибку не возвращать, то просто пробрасывать принятые деньги на кошелёк атакующего, что вернуть свою ставку. Ну, либо там мог быть бесконечный цикл, вот пример https://twitter.com/0xInuarashi/status/1517677597978599425

Вроде бы, всё ок, то есть возвраты стали работать и участники аукциона свои деньги назад таки получили.

Но есть и другой неприятный момент. В коде для функции claimProjectFunds, которая выводила деньги на счёт устроителей, было условие refundProgress >= totalBids (то есть нужно, чтобы было выплачено возвратов столько, сколько было ставок, видимо) - только после этого сами владельцы контракта могут забрать вырученные средства, то есть победившие ставки.

Однако дополнительно была введена переменная bidIndex, которая тоже была связана с числом ставок. В функцию bid, приходила не только денежная сумма, а ещё и количество ставок (amount), которое могло варьироваться от 1 до 3. Но владельцы контракта это не учли и вместо того, чтобы написать bidIndex += amount написали bidIndex++ (ну, либо им надо было ещё одну переменную выделять на это).

А дальше самое интересное. В функции для обработки возвратов стояло ещё одно условие require(_refundProgress < _bidIndex, "Refunds already processed");. То есть кол-во возвратов не могло превышать кол-во bidIndex. Но если вспомнить, что bidIndex считается неправильно (а многие юзеры делали сразу много ставок), то refundProgress всегда будет меньше, чем totalBids (то есть кол-во ставок в принципе, которое как раз считалось правильно). А теперь мы вспомним условие в функции "забрать себе деньги за аукцион": require(refundProgress >= totalBids, "Refunds not yet processed");.

Фактически, это условие истинным не будет никогда, то есть устроители аукциона обманули самих себя, и деньги застряли на их же контракте.
👍9🔥4
DROP TABLE
😁8🔥1
😁202👍2
А, и кстати: сегодня же день "Звёздных войн" 😄
🔥92👍1🎉1
Я всё ещё эпизодами дочитываю в очередной раз Тэффи и нашёл такой момент:

Портрет мой, нарисованный Репиным, получился какой-то волшебно-нежный, совсем неожиданный, не похожий на могучую лепку репинской кисти.
Он обещал его мне. Но ко мне он так и не попал. Был послан на выставку в Америку и, по словам Репина, застрял в таможне.
Мне неловко было расспрашивать и настаивать. Мне говорили:
— Он не хочет признаться, что продал его.
Все равно. Он пропал бы во время революции, как пропали все мои портреты и многие любимые мною вещи, без которых, казалось бы, и жить не стоило…


Интересно, как у разных людей разнятся мысли. Я никогда не привязывался ни к вещам, ни к месту (хотя, конечно, у Тэффи "казалось бы" - тоже намёк на то, что не всё так просто).

Когда-то, в другой жизни, я уехал практически без ничего. Сумка с компьютером, который спустя год всё равно пришлось собирать заново посреди ночи из тех запчастей, что удалось купить в срочном порядке, сумка с кошкой, стойка и микрофон, какой-то минимум одежды...

А потом как-то обживаешься, и в общем-то не скучаешь по тому, что осталось где-то там, от чего и так уже в душе давным-давно отказался. То же самое можно купить опять, это ведь не проблема. Вещей не жаль. Жаль людей.

И тут вспоминается Сапковский:

- Co ty mozesz wiedziec, Itka - miauknela cicho, zlowrogo kotka. - Zal? Moze i tak, zal mi ich. Zal mi dotyku ich rak. Zal mi szmeru ich oddechów, gdy spia. Zal mi ciepla ich kolan. Zal mi naszej muzyki, która, ledwo poznana, trace.


Действительно жаль. Когда теперь ещё увидимся?

Но остаётся работа, остаются мысли о том, что, насколько это возможно, мы можем делать мир лучше - хотя бы для кого-то. Отзывы зрителей - это лучшая награда.

А теперь почему-то вспоминается давнее турне по Италии и Франции, которое удалось устроить лет... хотел написать 5-6, но, кажется, уже 6-7 назад.

Тогда мы как раз и посетили Русское кладбище в пригороде Парижа, поклонились могиле Тэффи, Нуреева, участникам белого движения... К счастью, родственники помогли обустроиться, так что мы прожили во Франции почти неделю.

В один день зашли в какой-то бар и почему-то @ailura24 улетела с одного коктейля (алкоголя налили много?). Ходили по всему Версалю как два шатающихся дурака. Глупо ведь, а весело. Потом уже ночью гуляли все вместе, какие-то весёлые парижане кричали нам с балкона, приглашали на тусовку к ним домой.

Причём ведь сейчас тоже можно повторить что-то подобное, даже гораздо проще, но как-то душа не лежит. Тяжело. Хочется быть эдаким рубаха-парнем, а выходит мужик неопределённого возраста, который наговаривает заранее неотрепетированный текст в микрофон (впрочем, текст не репетировался никогда) скрипучим голосом, а два раза в неделю избивает грушу в зале. Да, и мучает студентов раз в неделю на семинарах. Впрочем, это ведь на пользу.

В любом случае, не нужно опускать руки. Мы живы, мы здесь. Adsumus. Adsumus in nomine tuo.
👍17🤔2👎1🤯1😢1
Друзья, возник справедливый вопрос по атаке типа reentrancy и функциям transfer/send - являются ли они гарантией безопасности?

Так вот, reentrancy в теории может быть везде, где есть вызов сторонних контрактов. Желательно априори считать вызов сторонних неизвестных контрактов небезопасным.

Функции transfer/send действительно имеют жёсткий лимит по газу (2300), но и в него тоже можно уместиться и сделать что-нибудь нехорошее. Вообще-то, раньше transfer действительно считалась безопасной функцией (её и ввели для защиты от reentrancy), но только никто не учёл, что стоимости по газу могут меняться. Если раньше "втиснуться" в лимит 2300 особо было нельзя, то теперь вполне можно (стоимости упали). Например, вот тут немного подробнее https://immunebytes.com/transfer-in-solidity-why-you-should-stop-using-it/ Короче, нужно быть осторожными
👍12
Для "развлечения" сделал скрипт, который подбирает "красивые" адреса контрактов, которые разворачиваются через create2. То есть просто подбирается соль. Код нужно сильно оптимизировать, но всё же это работает:

function stringGen(len) {
var text = "";

var charset = "abcdefghijklmnopqrstuvwxyz0123456789";

for (var i = 0; i < len; i++)
text += charset.charAt(Math.floor(Math.random() * charset.length));

return text;
}

const func = async function (hre) {
const {deployments, getNamedAccounts} = hre;
const {deploy} = deployments;

const {deployer} = await getNamedAccounts();

while(true) {
let salt = stringGen(Math.floor(Math.random() * 30))
let res = await deploy('TestContract', {
from: deployer,
log: false,
deterministicDeployment: hre.ethers.utils.solidityKeccak256(["string"], [salt]),
});

if(res.address[2] === res.address[3] && res.address[2] === res.address[4] && res.address[2] === res.address[5]) {
console.log(res.address, salt)
break;
}
}

};

export default func;

К примеру, с солью u8phmitlg9hse17xin8 и небольшим демо-контрактом выходит адрес 0x88880b14BDFF750Ce5B58d1A55fCa65403BdBaaE (четыре восьмёрки в начале). Скрипт работает с помощью hardhat-deploy
👍10🔥1😁1
"Hello world" на разных языках программирования (примеров сотни). Кстати, Solidity нет, поэтому желающие могут добавить 😄 http://helloworldcollection.de/
Любопытная мысль об ООП в целом:

I really like the way Bruce Richardson put it:

nobody slicing bread thinks they’re sending a message telling the knife to cut the bread or telling the bread to be sliced

OOP is about putting some awkward constraints on the way that you are allowed to use your language. Perhaps in some cases it makes sense, but in most cases it doesn’t. If OOP is about tying some actions to some objects, then if someone wants to perform some action that wasn’t anticipated by the designer, they have a problem.

Therefore OOP forces you to come up with solutions to issues that appear solely because someone decided to use the constraints of OOP in your project, like “which class should this method belong to?” (“oh I know I should create a controller class”).

I believe the only approach that actually scales is trying to express your way of thinking about problem, rather than imposing some arbitrary and artificial constraints beforehand. Solving problems that could be avoided is just a waste of time.


А речь о том, что написание красивого и понятного кода - это искусство, а не ремесло. В некоторых видео (каюсь, в основном только про Ruby) я пытаюсь донести эту мысль. Нужно немного расширить границы и делать не так, как учили (тем более, что обычно вообще никого не "учили"), а как кажется правильным. Да, потом это может оказаться неправильным и неоптимальным, но всё можно переписать. Зато в один прекрасный день вы увидите, что не ЯЗЫК управляет ВАМИ, а ВЫ управляете ЯЗЫКОМ. В конце концов, для кого вообще создавались языки программирования? Уж точно не для железки, которая понимает только нолики и единички (ну, в отдельных случаях бывает ещё троичная логика, да).

Долгое время понять это довольно сложно, потому что выход за рамки приводит к постоянно ломающимся программам - у меня так тоже было довольно долгое время. Но потом - бац - и что-то перещёлкивает, и ты смотришь на это всё несколько иначе.
👍15🔥1
И последняя мысль на сегодня, но тут, скорее, просто заметка или памятка для себя. Связана эта мысль с тем, почему вообще я делаю то, что делаю (а вы это спрашивали уже несколько раз)? Ради чего? Ну, или почему я общаюсь с теми, с кем общаюсь? Можно ведь просто заниматься рабочими вопросами. А что - работа есть, задачи есть, а вечером можно расслабиться.

Ну, наверное, потому, что все эти люди, которые смотрят мои видео, не гонят меня прочь, не опускают глаза, отвечают мне, хотя далеко не всегда соглашаются с моей точкой зрения (это вовсе не значит, что на основной работе есть какие-то проблемы, речь тут о другом). Потому что я чувствую здесь себя "в теме". Как это глупо звучит, да?

Но ведь все мы родом из детства, а в детстве приходилось пережить много чего. Да и не в детстве тоже: вообще довольно сложно найти кого-то, кто тебя поймёт. Именно поэтому мне так нравится высказывание моего бывшего начальника: "Счастье - это когда тебя понимают и когда ты нужен".

Jadę z nimi, bo jestem bezwolny golem. Bo jestem wiecheć pakuł gnany wiatrem wzdłuż gościńca. Dokąd, powiedz mi, mam pojechać? I po co? Tutaj przynajmniej zebrali się tacy, z którymi mam o czym rozmawiać. Tacy, którzy nie przerywają rozmowy, gdy podchodzę. Tacy, którzy nawet nie lubiąc mnie, mówią mi to w oczy, nie rzucają kamieniami zza opłotków. Jadę z nimi z tego samego powodu, dla którego pojechałem z tobą do flisackiej oberży. Bo jest mi wszystko jedno. Nie mam miejsca, do którego mógłbym zmierzać. Nie mam celu, który powinien znajdować się na końcu drogi.
👍165
Напоминалка по JWT
👍204🔥1
В этом уроке мы поговорим о низкоуровневых вызовах call и delegatecall, узнаем, в чём их отличие, как работать с возвращаемыми ими данными. Посмотрим, как работать с encodeWithSignature и encodeWithSelector, а также узнаем, к каким проблемам безопасности может привести использование delegatecall. https://www.youtube.com/watch?v=QzdMZbcn3o4
👍20
Говорят, с апдейтом Hardhat и glob какие-то проблемы (не проверял ещё)
Forwarded from Roman Brix
I finally found it.
glob@7.2.2 <- This is problem

I haven't looked for it exactly from what point, but it's version 7.2.0 in the package at the point where that package works well, and version 7.2.2 in the "nothing to compile" version.

Type the "npm intstall glob@7.2.0" into an empty project and copy /node_modules/glob
copy /node_modules/glob to YOUR_HARDHAT_PROJECT/node_modules/hardhat/node_modules/glob
It works.