Русский
Русский
English
Статистика
Реклама

Solidity

Dapp. Vue.js ethers.js

10.04.2021 12:12:31 | Автор: admin

Введение

В этой статье я попытаюсь максимально кратко и информативно пройтись по всем пунктам создания создания полноценного Децентрализованного приложения в сети Ethereum используя JavaScript фреймворк - Vue для создания веб-приложения и библиотеку ethers.js для общения со смарт контрактом. Также будут рассмотрены некоторые моменты о том, как установить и подключить кошелек, как задеплоить контракт в сеть используя truffle и др.

Некоторые пункты могут показаться очень очевидными и не сложными, потому вы их можете пропустить.

Что уже нужно знать перед тем как начать:

  • Неплохие знания js, в частности Vue.js

  • Понимание принципов работы Blockchain (смарт-контрактов)

  • Базовые знания языка пр. Solidity

Установка кошелька

Для подтверждения транзакций, общения с контрактом и проверки некоторых параметров этих транзакций мы будем использовать кошелек Metamask. Он довольно хорош и с его работой у меня почти никогда не возникало проблем. Процесс установки я показывать не буду, там все очевидно.

Когда расширение установится - вас попросят создать кошелек или импортировать если такой имеется. После создания аккаунта у вас сгенерируется мнемоническая фраза. НИКОГДА, НИКОМУ ее не разглашайте. И воаля - у нас есть готовый адрес кошелька)

Так как мы будет разрабатывать пример приложения - лучше использовать тестовую сеть, дабы не тратить реальные деньги на развертывание контракта и и выполнение транзакций. Для нашей задачи отлично подойдет Ropsten.

Здесь вы можете нафармить себе немного тестовых эфиров, чего нам хватит для работы.

Контракт

Пока не на долго забудем о нашем кошельке и приступим к написанию смарт-контракта. Для примера я буду использовать уже готовый контракт. Более подробно о нем вы можете ознакомиться с ним в README (он довольно простой). Основная идея - это купля-продажа. Продавец выставляет свой товар - покупатель ставит свою цену и делает предложение, продавец в свою очередь принимает предложение. Много нюансов упущено, но для для примера сойдет. Попробовать попользоваться контрактом вы можете в бесплатной IDE Remix.

Развертывание контракта

Для этой задачи мы будем использовать Truffle.js. Для его установки на вашей машине должен быть уже установлен node.js. Дальше открываем любой вам удобный редактор и в консоли через npm устанавливаем следующие пакеты (в моем случаи глобально): npm install -g truffle и npm install -g @truffle/hdwallet-provider После чего создайте папку, где будет хранится ваш проект и инициализируйте Truffle проект в нем командой truffle init.

В папке с проектом с начала обратим наше внимание на файл truffle-config.js. В нем хранятся конфигурации по развертыванию контрактов. Но прежде всего нам нужно будет зарегистрироваться в Infura и создать новый проект перейдя по вкладке Ethereum -> Create New Project соединяет интерфейс пользователя (UI) dApps с внешним смарт-контрактом на блокчейне Ethereum. Провайдер Infura может обрабатывать подписание транзакции, а также подключение к сети Ethereum без необходимости синхронизировать ноду и это именно то, что нам нужно. Дальше выбрав нужный созданный проект -> Settings -> Endpoints выбираем нашу сеть Ropsten и копируем указанный адрес.

Теперь можно перейти, непосредственно, к настройке truffle-config.js. Во-первых, создайте в корневой папке проекта файл .secret в котором будет хранится наша мнемоническая фраза. Лучше всего использовать dotenv, но для ознакомления оставим так, как предлагает truffle. Во-вторых, раскомментируйте следующие строки:

const HDWalletProvider = require('@truffle/hdwallet-provider');const fs = require('fs');const mnemonic = fs.readFileSync(".secret").toString().trim();

и также

ropsten: {    provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`),    network_id: 3,       // Ropsten's id    gas: 5500000,        // Ropsten has a lower block limit than mainnet    confirmations: 2,    // # of confs to wait between deployments. (default: 0)    timeoutBlocks: 200,  // # of blocks before a deployment times out  (minimum/default: 50)    skipDryRun: true     // Skip dry run before migrations? (default: false for public nets )    },

И вместо второго параметра в HDWalletProvider поместите скопированный адрес.

В-третьих, создайте новый файл формата .sol в папке contracts, и поместите туда весь код контракта. В папке migrations создайте js файл и поместите туда следующий код:

const SimpleMarketplace = artifacts.require("SimpleMarketplace");module.exports = function (deployer) {    deployer.deploy(SimpleMarketplace, 'description', 1000);};

Обратите внимание, что при создании нового файла в migrations название файла должно начинаться с цифры следующей по порядку от файла 1_initial_migrations.js, то есть 2.

Так как у нас контракт содержит конструктор с параметрами - мы методе deploy вторым и третьим аргументами ставим именно их: description и price в нашем случае. Если у вас контракт без конструктора или с конструктором, но без параметром, то оставляем просто переменную SimpleMarketplace.

Сохраняем и в терминале с проектом пробуем скомпилировать контракт введя команду truffle compile. Важно: Если у вас выкидывает ошибку, что не найден пакет @truffle/hdwallet-provider,(Error: Cannot find module '@truffle/hdwallet-provider'),то попробуйте установить этот пакет в текущую директорию командой npm install @truffle/hdwallet-provider. без опции-g и скомпилировать снова. После вывода в консоль Compiled successfully using... можно пробовать деплоить.

Для развертывания контракта в сети в консоль введите truffle migrate --network ropsten --reset. Где ropsten - это название сети, что мы указали в файле truffle-config.js, а --reset опция, которая просто передплоивает контракт наново если вы уже выполняли подобные действия (при первом разе можно и без нее).Важно: Если у вас выбрасывает подобную ошибку: var e = new Error('ETIMEDOUT'), то попробуйте в truffle-config.js в HDWalletProvider вставить другой адрес, что предлагает Infura - wss://ropsten.infura.io/ws/v3/YOUR-PROJECT-ID.

Если процесс прошел без ошибок и в Etherscan перейдя по своему адресу вы можете наблюдать транзакцию, о создании контракта - поздравляю, контракт находится в сети blockchain и можно идти дальше.

Создание Vue.js приложения

И так, если все выше перечисленные шаги вам удалось произвести, то можно начинать писать веб-приложения для нашего контракта. Установку vue/cli и создание проекта я упущу, так как это детально описано в официальной документации Vue.js. Установка. Создание проекта.

После того как ваше приложение было создано можно начать писать саму логику. Для начала давайте удалим из все assets (в нем будет хранится логотип Vue.js, который нам не нужен), удалим компонент HelloWorld.vue из components и в файле App.vue уберем все лишнее (импорт компонента HelloWorld.vue, саму инициализацию компонента в объекте components и оставим чистый шаблон с <div id="app">внутри).

<template>  <div id="app">  </div></template><script>export default {  name: 'App',  components: {  }}</script><style>#app {  font-family: Avenir, Helvetica, Arial, sans-serif;  -webkit-font-smoothing: antialiased;  -moz-osx-font-smoothing: grayscale;  text-align: center;  color: #2c3e50;  margin-top: 60px;}</style>

Запустив проект командой npm run serve можете проверить, что все работает и нет никаких ошибок, как и в прочем, ничего вообще пока что.

Чтобы взаимодействовать с таким простым контрактом нам понадобится всего лишь один новый компонент - это главная страница на которой мы будем делать UI для нашего смарт-контракта. В папке components создайте новый компонент и назовите его, к примеру, Marketplace.vue. Так же создайте папку core и в ней файл core.js и в проекте, в котором мы развертывали контракт, перейдите в папку ./build/contracts и скопируйте от туда JSON - файл с названием вашего файла контракта (или же запомните путь к нему). Этот файл - это как-бы инструкция, которая говорит вам как можно обратится к контракту. Не обязательно создавать все эти папки и называть так файлы, но это просто считается хорошим тоном. В дальнейшем хорошо структурированный проект будет проще поддерживать. Перед тем как продолжить - установите в пакет npm install ethers.

В файле core.js проимпортируйте установленную библиотеку, укажите путь в JSON - файлу и возьмите оттуда адрес нашего контракта:

const { ethers } = require('ethers')const ContractArtifact = require('./SimpleMarketplace.json')const CONTRACT_ADDRESS = ContractArtifact.networks['3'].address

Дальше создадим переменную ABI, которая должна быть массивом строк и занесем туда все необходимые нам функции контракта, что мы будем использовать (Вы можете просто скопировать строку из контракта и занести сюда. Если в контракте просто public поле, для него можете тоже отдельно написать public view returns функцию и ethers не должен на это ругаться):

const ABI = [    'function InstanceOwner () public view returns(address)',    'function Description () public view returns(string)',    'function AskingPrice () public view returns(int)',    'function InstanceBuyer () public view returns(address)',    'function OfferPrice () public view returns(int)',    'function MakeOffer(int offerPrice) public',    'function Reject() public',    'function AcceptOffer() public']

После чего нам нужно определить ряд переменных:

let provider = new ethers.providers.Web3Provider(window.ethereum)//С помощью провайдера мы подключаемся к сети Blockcainlet readOnlyContract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider)//Дает нам возможность читать view методы контрактаlet signer = provider.getSigner()//Нужен для подтверждения транзакцийlet contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer)let contractSigner = contract.connect(signer)//Дают возможность выполнять транзакции

Дальше определим методы которые будем вызывать во Vue компонентах:

export default {    async getDescription() {        const description = await readOnlyContract.Description()        return {description: description}    }}

И таким образом для каждой функции, с которой хотите взаимодействовать определяете свой метод. В итоге должно получится что-то вроде:

const { ethers } = require('ethers')const ContractArtifact = require('./SimpleMarketplace.json')const CONTRACT_ADDRESS = ContractArtifact.networks['3'].addressconst ABI = [    'function InstanceOwner () public view returns(address)',    'function Description () public view returns(string)',    'function AskingPrice () public view returns(int)',    'function InstanceBuyer () public view returns(address)',    'function OfferPrice () public view returns(int)',    'function MakeOffer(int offerPrice) public',    'function Reject() public',    'function AcceptOffer() public']let provider = new ethers.providers.Web3Provider(window.ethereum)let readOnlyContract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider)let signer = provider.getSigner()let contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer)let contractSigner = contract.connect(signer)export default {    async getInstanceOwner() {        const instanceOwner = await readOnlyContract.InstanceOwner()        return {instanceOwner: instanceOwner}    },    async getDescription() {        const description = await readOnlyContract.Description()        return {description: description}    },    async getAskingPrice() {        const askingPrice = await readOnlyContract.AskingPrice()        return {askingPrice: askingPrice}    },    async getInstanceBuyer() {        const instanceBuyer = await readOnlyContract.InstanceBuyer()        return {instanceBuyer: instanceBuyer}    },    async getOfferPrice() {        const offerPrice = await readOnlyContract.OfferPrice()        return {offerPrice: offerPrice}    },    async makeOffer(offerPrice) {        const txResponse = await contractSigner.MakeOffer(offerPrice, {gasLimit: 300000})        const txReceipt = await txResponse.wait()        return {transaction: txReceipt.transactionHash}    },    async reject() {        const txResponse = await contractSigner.Reject({gasLimit: 300000})        const txReceipt = await txResponse.wait()        return {transaction: txReceipt.transactionHash}    },    async acceptOffer() {        const txResponse = await contractSigner.AcceptOffer({gasLimit: 300000})        const txReceipt = await txResponse.wait()        return {transaction: txReceipt.transactionHash}    }}

Дальше я в файле App.vue в mounted методе вызываю подключение кошелька и в свойство $root заношу проимпортированный заранее наш файл core.js:

const core = require('./core/core')/*Какой-то другой код*/mounted() {    window.ethereum.request({ method: 'eth_requestAccounts' })    this.$root.core = core.default  }

Дальше под каждое поле, что вы хотите читать с контракта создаете поле в в data и метод details в объекте methods :

data() {    return {      instanceOwner: '',      description: '',      askingPrice: '',      instanceBuyer: '',      offerPrice: ''    }  },  methods: {    async details() {      this.instanceOwner = (await this.$root.core.getInstanceOwner()).instanceOwner      this.description = (await this.$root.core.getDescription()).description      this.askingPrice = (await this.$root.core.getAskingPrice()).askingPrice      this.instanceBuyer = (await this.$root.core.getInstanceBuyer()).instanceBuyer      this.offerPrice = (await this.$root.core.getOfferPrice()).offerPrice    }  },

Тем временем в разметке выводим необходимые нам поля:

<button v-on:click="details">Get details</button><h3>Instance owner: {{ instanceOwner }}</h3><h3>Description: {{ description }}</h3><h3>Asking price: {{ askingPrice }}</h3><h3>Instance buyer: {{ instanceBuyer }}</h3><h3>Offer price: {{ offerPrice }}</h3>

Код компонента Marketplace.vue можете проверить в моем репозитории, дабы не захламлять статью лишним кодом.

После запуска проекта у вас должно появится окно подключения кошелька подобно этому:

И после этого при нажатии на кнопку Get details мы получим данные, которые вводили при развертывании контракта.

А так выглядит вывод если произвести транзакцию:

Заключение

Это моя первая статья. Буду рад каким-то вопросам и даже критике, так как я сам пока не профи во всем этом.

Ссылка на контракт, где вы можете проверить транзакции.

Подробнее..
Категории: Javascript , Vuejs , Solidity , Blockchain , Dapp

Сказки про NFT для самых маленьких

20.04.2021 20:11:13 | Автор: admin

"Закопай свои монеты в Открытом Море и к утру разбогатеешь" Криптокот Базилио

Гифка за 580 000$, набор пиксельных панков за 7.5mil$ и Kings of Leon выпускающие свой альбом прямо на нем. О дивный новый мир искусства и какого черта в нем вообще происходит?

Аве Кодер!

Сегодня я расскажу вам сказку о невиданной широкой публике зверушкой, а именно про так называемые NFT и разумеется нас будет особенно интересовать техническая сторона вопроса ну и на протяжении всей истории, я конечно же, как винтажный газогенератор, буду удивляться - куда катится мир. <cut/>

Итак NFT или Non-Fungible Token, то есть Незаменимый Токен представляет из себя , по сути, набор цифровых данных на блокчейне.

И нужен для того, чтобы закрепить за владельцем право владения определенной единицей так называемого цифрового искусства - будь то гифка, джейпег или мптри.

Аналогию которую обычно приводят для объяснения что значит незаменимый токен, обычно включает в себя сравнение с тем же биткоином или валютой, как эталоном чего-то взаимозаменяемого, что, мол если я дам вам свои сто баксов, а вы мне свои, то мы в итоге оба останемся со ста баксами, это будут другие сто баксов нежели у нас были изначально, но их ценность от этого не поменяется.

Возможно от этого объяснения день не становится ясней, а все потому, что сама концепция владения чем-либо в мире цифровых реалий немного искажена.

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

С цифровым искусством все одновременно так и не так. Художник создает свою картину сразу в цифре, то есть в привычном нам графическом формате.

Эта картина, попадая в интернет уже становится легкодоступной кому попало - ее можно легко скачать и та самая картинка в формате джепег, которую я скачал посредством правого клика мыши будет абсолютно такой же как и та же картинка у какого-нибудь Мясистого Джо из Гваделупы, потому что в Гваделупу также завозят мышки с двумя кнопками.

Так вот этот самый NFT токен, привязанный к картинке и дает такое же право собственности, которое дает какой-нибудь аукцион Сотбис обладателю оригинала Дали, разве что вместо картины известного мастера у себя над камином, твой джепег может также продолжать висеть в интернете, я и Мясистый Джо также можем использовать ее как заставку на рабочем столе, только теперь весь мир знает, что на самом деле, принадлежит она только тебе, а у нас всего лишь копии.

Ну и что, скажете вы, я купил билет на концерт или уникальный скин в игре, я уже обладаю своим незаменимым предметом, я могу его продать или обменять. Да, все это так, и вот тут начинается разговор о потенциале самой идеи.

Дело в том, что такие токены и соответственно право владения цифровой единицей размещаются на публичном Блокчейне. То есть в теоретическом будущем, если технология NFT реализует свой потенциал такие токены будут способны перемещаться по всей экосистеме; если совсем на пальцах, то вы не будете заперты внутри той маленькой экосистемы, в которой вы приобрели свой цифровой продукт, например заходить в игровой обменник, чтобы продать кому-то скин, а сколько было случаев, когда кто-то брал билет у перекупщиков и уже на входе выяснял,что это подделка? Подразумевается, что блокчейн и система токенов навсегда решат проблему с ликвидностью и контрафактами, но пока это только-только зарождается.

А началось все с того самого криво нарисованного лягушонка Pepe и так называемых цветных монет, что было одной из первых попыток представления реальных активов при помощи блокчейна Биткоина.

Затем появляются пиксельные CryptoPunks, коллекция в десять тысяч голов, каждая из которых представляла собой уникальный коллекционный арт объект. И их токены уже гоняли на этом вашем Эфириуме.

И если не все оценили криптопанков, то как насчет котиков? Именно с запуском CryptoKitties в 2017 году по сути и начинается отсчет прихода NFTишек в мейнстрим. Котики делились на поколения, первый кот пришедших вмейнстрим был мейн-кун, шутка! На самом деле их было даже несколько и все они принадлежали к так называемому поколению 0.

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

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

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

Итак, теперь о технической стороне вопроса. На чем же можно эти токены создавать? Поскольку блокчейн изначально затачивался под Ethereum, то и стандарты самых популярных умных контрактов написаны на Solidity, который очень подозрительно похож на JavaScript.

Стандарт ERC721, популяризированный CryptoKitties позволяет с легкостью сопоставлять уникальные идентификаторы, каждый из которых представляет собой единичный цифровой актив с адресом владельца этого идентификатора, а также позволяет передачу актива посредством метода transferFrom. Также можно пробить и владельца определенного контракта, при помощи метода ownerOf.

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

Но вернемся к тому, как именно определяется владелец определенного токена. Например, мы видим, что некий адрес является владельцем криптокотенка за номером 123, откуда нам знать как выглядит то, что определяет номер этого токена, хотя бы название или уникальные атрибуты?

Тут-то в дело и вступает метадата. Метадата - это что-то вроде данных о данных, небольшая надстройка, описывающая дополнительные атрибуты, очень часто в читаемом для людей виде.

В случае с криптокотами, метадата содержит имя кота, картинку кота и прочие дополнительные котрибуты.

В случае с билетом на мероприятие, метадата может содержать дату проведения, тип билета, возможно имя владельца, место и прочее.

Помимо этого, метадата может быть впечатана в сам умный контракт или содержаться отдельно, то есть быть в-блокчейне или вне-блокчейна.

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

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

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

Ну а пока, стандарт ERC721, к примеру, имеет метод tokenURI, который как раз и указывает на места хранения метаданных.

Итак, как же нам создать свой NFT токен? Естесственно можно заморочится, включить свою любимую IDE и освоить Solidity. Если вы владеете JavaScriptom, то для вас это будет что-то вроде освоения Болгарского языка после Русского.

Кстати процесс создания токена называется minting, то есть чеканка. Да, мальчик, mint это не только мята, но и чеканка и первый класс и свежесть и еще много чего.

Так вот, можно конечно делать все это самому, а можно отправиться на спец площадку занимающуюся размещением токенов. Их есть достаточно, с различной политикой обогащения. Когда я попытался оценить стоимость чеканки и размещения моего токена на Rarible, созданной двумя российскими энтузиастами, то общий счет был порядка шестиста долларов сша в эфириумах.

Open Sea, которая является и самой крупнейшей берет порядка 60-ти баксов за открытие счета и 40-ка за минт. Есть оптимистичные новости, что летнее обновление ethereum блокчейн поможет существенно снизить эти затраты, но это, как говорится, только мечты.

И что мы по итогу имеем? Дабы огорчить всех тех, кто придет гадить в комменты с криками: что ты такое говоришь, V, там все совсем по-другому, ты не понимаешь смысл пустошей и прочую ерундистику, вот вам еще одна аналогия.

Представим, что вы купили себе пазл на тысячу частей и решаете разместить картинку пазла на блокчейне, то есть на дистрибутивной базе данных, коей он по-сути и является.

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

Теперь вы сидите такой дома и любуетесь на картинку на коробке из-под пазла.

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

Послесловие

Очень скоро соцсети заполонят мальчики и девочки еще вчера предлагавшие заработать на тотализаторе или форексе с предложением купить у них курс о заработке на NFT, причем обязательно надобностью без владения оными, все как они любят.

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

История много раз доказывала, что прежде чем технология становится чем-то действительно полезным, она проходит полный цикл, от непонимания, до надувания финансового пузыря, до разочарования когда он лопается и в финале, когда остается лишь суть, человек думает, что с этим на самом деле делать. Если ничего хорошего не получается придумать, либо если общий технологический или социальный прогресс не позволяет пока раскрыть весь ее потенциал, она просто откладывается в долгий ящик, до тех пор когда очередной Илон Маск не достанет ее оттуда и не попробует сделать новый гиперлуп.

А это был V, до новых встреч!

P.S. то же самое, только моим заунывным голосом и под веселые картинки:

Подробнее..

Ненормальная криптография, или как я проверял подписи Ed25519 на Solidity

06.10.2020 10:05:04 | Автор: admin

Когда пишут о том, как разрабатывать безопасные приложения, один из частых советов звучит так: не пишите криптографию самостоятельно, а используйте какую-нибудь проверенную библиотеку. К сожалению, при разработке блокчейнов этот совет часто не работает: нужные алгоритмы либо не реализованы вовсе, либо существующие реализации не годятся по множеству возможных причин даже потому, что они недостаточно безопасны. Вот и в этом случае нужно было проверять подписи, использующие Ed25519 весьма популярный тип подписей, для которого существует множество реализаций, ни одна из которых, однако, нам не подошла. А всё потому, что проверка должна была выполняться из смарт-контракта, работающего на блокчейне Ethereum.


Что такое смарт-контракт

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


В чём же проблема?


В Ethereum смарт-контракты выполняются в специальном окружении так называемой виртуальной машине Ethereum (Ethereum virtual machine, EVM).


Что такое EVM

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


EVM разработана, чтобы быть простой в реализации, но ценой этого является низкая эффективность. Кроме того, все вычисления на блокчейне повторяются всеми узлами, поэтому стоимость этих вычислений на порядки больше, чем, например, стоимость вычислений на облачном сервере. По этой причине смарт-контракты приходится тщательно оптимизировать. Поскольку EVM существенно отличается от окружения, в котором работает большинство программ, программы, написанные на обычных языках программирования, не работают в EVM. Если бы обычные языки и допускали трансляцию в EVM-байткод, из-за различий архитектуры код практически любой реализации Ed25519 был бы слишком неэффективен настолько, что ресурсов, выделяемых на обработку одной транзакции, не хватило бы даже, чтобы проверить одну подпись. Вместо них, для EVM разработано несколько специальных языков, и самый популярный из них Solidity.


EVM существенно отличается как от популярных процессорных архитектур, так и от часто используемых абстрактных виртуальных машин, таких как JVM или WASM. В то время как большинство архитектур содержат операции для работы с 32- и 64-битными числами, в EVM единственный тип данных 256-битное целое число. Для реализации Ed25519 это очень удобно, так как все необходимые вычисления производятся над числами, помещающимися в 256 бит.


Как устроена подпись Ed25519

Ed25519 подпись на основе схемы Шнорра и эллиптической кривой Curve25519. Точки на кривой задаются парами координат $(x, y)$, причём вычисления с координатами производятся по модулю простого числа $p$, равного $2^{255}-19$ отсюда и название кривой. Для точек определена операция сложения, вместе с которой множество точек образует циклическую группу, размер которой равен $8\cdot l$, где $l=2^{252}+27742317777372353535851937790883648493$ простое число. При этом используется только подгруппа размера $l$.


Через операцию сложения точек можно эффективно реализовать операцию умножения точки на число. Обратная же операция поиск числа, которое, будучи умноженной на заданную точку, даст другую заданную точку не имеет известного эффективного решения. На этом основана безопасность системы: закрытый ключ это число $a$, а открытый точка $A=a\cdot G$ ($G$ фиксированная точка, имеющая порядок $l$). Зная $a$, можно легко вычислить $A$, в то время как обратная задача, вероятно, не имеет эффективного решения (хотя строго это не доказано).


Для создания подписи Ed25519 необходимо сгенерировать случайное число $r$ и вычислить точку $R=r\cdot G$. Затем вычислить хеш SHA-512 от конкатенации $R$, $A$ (открытого ключа) и подписываемого сообщения: $h=H(R||A||m)$ (здесь $H$ хеш-функция, а $||$ обозначает конкатенацию, чтобы не путать со сложением). Затем вычислить $s=r+ha\bmod l$ в этом месте необходим закрытый ключ. Значением подписи будет пара $(R,s)$. На самом деле $r$ генерируется не случайно, а путём хеширования сообщения с частью закрытого ключа, но это делается для того, чтобы не зависеть от наличия безопасного генератора случайных чисел, и не влияет на правильность подписи.


Для проверки подписи нужно также вычислить $h=H(R||A||m)$ и проверить, что $s\cdot G=R+h\cdot A$. Несложно убедиться, что любая правильно сгенерированная подпись гарантировано пройдёт проверку. Предполагается, что, не имея закрытого ключа, сгенерировать правильную подпись практически невозможно, даже имея несколько существующих подписей.


Другое важное отличие EVM стоимость операций. В то время как в обычных виртуальных машинах время выполнения отдельных инструкций может различаться от реализации к реализации, в EVM точное количество используемых ресурсов является частью спецификации, ведь за выполнение смарт-контрактов нужно платить пропорционально затраченным ресурсам. Затраты ресурсов измеряются величиной, называемой газ (gas), и, зная затраты газа для выполнения каждой инструкции, можно точно определить, сколько будет стоить выполнение той или иной программы. И здесь кроется ещё один подвох: относительная стоимость отдельных инструкций существенно отличается от того, что стоило бы ожидать для обычной программы. Например, для обычной программы стоило бы ожидать, что умножение 256-битных чисел, тем более по модулю, занимает на порядок больше времени, чем сложение. А в EVM эти операции стоят одинаково. Значит, обычные криптографические алгоритмы, которые оптимизируют для минимизации количества умножений, для EVM могут быть не оптимальны.


Ещё EVM предусматривает раздельные области хранения данных: код, память и хранилище (storage). Память доступна на чтение и запись, но её содержимое не сохраняется между выполнениями смарт-контракта. Код доступен только на чтение. Хранилище доступно на чтение и запись, но доступ к нему, даже на чтение, намного дороже, чем доступ к коду или памяти. Оптимальная реализация Ed25519 использует таблицы заранее подсчитанных данных. Если держать их в хранилище, то при каждом использовании придётся считывать их оттуда, а это дорого. Оптимально держать их в коде, но для этого нужно генерировать код на Solidity программой на каком-то другом языке (хотя Solidity и позволяет вычислять значения во время выполнения конструктора контракта и зашивать их в код, ограничения этой функции делают её неподходящей для этой цели). Кроме того, компилятор Solidity не умеет производить некоторые нужные оптимизации (например, разворачивание циклов), которые можно решить генерацией исходного кода. Поэтому я решил генерировать код на Solidity программой на JavaScript.


Чтобы понимать, что написано дальше, стоит посмотреть JavaScript-код, который генерирует код на Solidity, а также сам сгенерированный код на Solidity.


Первый шаг: хеш-функция SHA-512


Первая вещь, которую должна сделать функция проверки Ed25519 посчитать хеш-функцию SHA-512. И даже это сделать нетривиально. В EVM доступна встроенная поддержка нескольких хеш-функций: SHA-256, Keccak-256 (почти SHA-3-256), даже древняя RIPEMD-160, а вот поддержки SHA-512 нет. И при попытке скомпилировать реализацию SHA-512 возникает проблема. EVM это стековая машина, и Solidity хранит все локальные переменные на стеке. Хотя стек может содержать до 1024 элементов, напрямую можно обращаться только к последним 16 из них. Если попытаться обратиться к локальной переменной, расположение которой на стеке не входит в одну из последних 16, то код не скомпилируется лично я считаю, что это серьёзная недоработка компилятора, но приходится пользоваться тем, что есть.


Вот псевдокод основной части SHA-512 обработки блока:


w[0..15] := блок сообщенияfor i in 16 .. 79:    s0 := (w[i-15] ror 1) ^ (w[i-15] ror 8) ^ (w[i-15] >> 7)    s1 := (w[i-2] ror 19) ^ (w[i-2] ror 61) ^ (w[i-2] >> 6)    w[i] := w[i-16] + s0 + w[i-7] + s1a, b, c, d, e, f, g, h := h0, h1, h2, h3, h4, h5, h6, h7for i in 0 .. 79:    S0 := (a ror 28) ^ (a ror 34) ^ (a ror 39)    S1 := (e ror 14) ^ (e ror 18) ^ (e ror 41)    ch := (e & f) ^ (~e & g)    maj := (a & b) ^ (a & c) ^ (b & c)    temp1 := h + S1 + ch + k[i] + w[i]    temp2 := S0 + maj    a, b, c, d, e, f, g, h := temp1 + temp2, a, b, c, d + temp1, e, f, gh0, h1, h2, h3 := h0 + a, h1 + b, h2 + c, h3 + dh4, h5, h6, h7 := h4 + e, h5 + f, h6 + g, h7 + h

Как видно, во втором цикле используется больше 16 переменных, а ведь ещё есть промежуточные значения, которые тоже занимают место на стеке. Чтобы получить работающий код, пришлось использовать множество ухищрений: использовать блоки, чтобы ограничить время жизни переменных, менять порядок переменных таким образом, чтобы нужные переменные были ближе к концу стека, менять структуру выражений. Заодно можно слегка оптимизировать выражения: вместо (e & f) ^ (~e & g) использовать (e & (f ^ g)) ^ g, а вместо (a & b) ^ (a & c) ^ (b & c) (a & (b | c)) | (b & c) (хотя можно и (a & (b ^ c)) ^ (b & c)). Для вычисления побитовых поворотов работает такой приём: чтобы вычислить побитовый поворот x, нужно взять число x | (x << 64) и вычислять его побитовые сдвиг (перед этим нужно очистить все биты x, кроме младших 64). Поскольку требуется вычислять по три побитовых поворота от одного и того же числа, выражение x | (x << 64) можно вычислить один раз и использовать три раза.


Поскольку в первом цикле для вычисления w[i] используются значения не новее w[i-2] (т. е. w[i-1] не используется), можно вычислять элементы w парами (использовать 256-битные операции как своеобразный SIMD). Тот же приём для побитовых поворотов работает, чтобы вычислять повороты от двух чисел сразу.


Общая схема алгоритма такая: использовать первые 16 значений w, затем вычислить следующие 16 значений, затем использовать их, и так далее. Сами значения w хранятся в четырёх переменных, упакованные по четыре, это позволяет избегать использования массивов. При этом внутренние циклы (по 16 итераций) развёрнуты, остаётся только внешний цикл, который выполняет 4,5 итерации.


Распаковка точек


Следующий этап: распаковка точек. Хотя точки на кривой Curve25519 задаются парами координат $(x, y)$, хранить обе координаты слишком расточительно: каждому значению $x$ соответствует не более двух значений $y$, и наоборот. Поэтому точки хранятся так: значение $y$ хранится целиком, а дополнительно хранится один бит, определяющий, какое из двух возможных значений $x$ нужно использовать. Поскольку значения координат берутся по модулю $p=2^{255}-19$, такая запись помещается в 32 байта. Однако, для вычислений нужны обе координаты, поэтому нужно восстановить значение $x$.


Значение $x$ восстанавливается по формуле $x=\pm\sqrt{(y^2-1)/(dy^2+1)}$. Поскольку $-d$ не является квадратичным вычетом по модулю $p$, значение знаменателя не может быть равно нулю, однако, значение квадратного корня существует только для чуть более половины возможных значений $y$. Здесь представляет интерес только деление и извлечение квадратного корня. Оказывается, эти операции можно совместить. Пусть необходимо вычислить $\sqrt{u/v}$, причём $v\ne0$. Для этого оригинальная статья Ed25519 предлагает такую процедуру (использующую тот факт, что $p\equiv5\pmod8$):


  • Посчитать $\beta=uv^3(uv^7)^{(p-5)/8}$.
  • Если $v\beta^2=u$, то ответ равен $\pm\beta$.
  • Иначе, если $v\beta^2=-u$, то ответ равен $\pm\sqrt{-1}\beta$.
  • Иначе, ответа не существует.

Почему это работает? Заметим, что $\beta^8=u^8v^{24}(uv^7)^{p-5}=u^{p+3}v^{7p-11}=(u/v)^4$, откуда следует, что $\beta^2=\pm u/v$ или $\beta^2=\pm\sqrt{-1}u/v$. Если выполняется второе равенство (и $u\ne0$), то квадратного корня из $u/v$ не существует, поскольку $\sqrt{-1}$ не является квадратичным вычетом. Если $\beta^2=-u/v$, то $(\sqrt{-1}\beta)^2=u/v$. Таким образом, в любом случае эта процедура находит ответ, если он существует.


Оказывается, эту процедуру можно слегка упростить: вместо $\beta=uv^3(uv^7)^{(p-5)/8}$ вычислять $\beta=u(uv)^{(p-5)/8}$. При этом $\beta^8=u^8(uv)^{p-5}=u^{p+3}v^{p-5}=(u/v)^4$, дальше доказательство полностью аналогично предыдущему варианту. Для этого вычисления используется вспомогательная функция, которая для произвольного значения $v$ вычисляет $v^{2^{250}-1}$ и $v^{11}$ по модулю $p$, эта же функция в дальнейшем используется для вычисления обратного по модулю $p$ (в этой функции реализована подобранная вручную аддитивная цепочка).


Двойное умножение


Вспомним, что для проверки подписи нужно проверить, что $sG=R+hA$. Кажется, что для этого нужно распаковать две точки ($R$ и $A$), но многие реализации, в том числе и эта, делают по-другому: вычисляют значение $sG-hA$, запаковывают и сравнивают с запакованным значением $R$. Таким образом, всё, что остаётся это вычислить $sG-hA$.


Для вычисления такого выражения есть несколько методов. Можно посчитать отдельно $sG$ и отдельно $hA$, но это неэффективно. Более эффективный метод удвоить и прибавить (double-and-add), в псевдокоде это выглядит примерно так:


result := 0for bit_index in n-1 .. 0:    result := double(result)    if s & (1<<bit_index) != 0:        result := add(result, G)    if h & (1<<bit_index) != 0:        result := subtract(result, A)

Здесь $n$ число бит в $s$ и $h$. Этот алгоритм сканирует биты в $s$ и $h$, начиная со старшего, и при обнаружении единичного бита прибавляет $G$ к текущему результату (или вычитает $A$). Этот алгоритм выполняет $n$ удвоений и $n$ (в среднем) сложений, если считать, что биты чисел $s$ и $h$ принимают значения $0$ и $1$ с равной вероятностью. Но и его можно ускорить. Заметим, что каждым сложением он покрывает один бит в числе $s$ или $h$, а можно сделать так, чтобы он покрывал сразу несколько. А именно, если для некоторого $k$ предпосчитать кратные $A$ и $G$ с коэффициентами от $2^k$ до $2^{k+1}-1$, то прибавлением такого кратного можно обработать сразу $k+1$ бит! Это выглядит примерно так:


result := 0for bit_index in n-1 .. k:    result := double(result)    if s & (1<<bit_index) != 0:        result := add(result, Gmul[s >> (bit_index-k)])        s := s & ((1 << (bit_index-k)) - 1)    if h & (1<<bit_index) != 0:        result := subtract(result, Amul[h >> (bit_index-k)])        h := h & ((1 << (bit_index-k)) - 1)

Этот алгоритм, обнаружив единичный бит, обрабатывает сразу и его, и $k$ следующих бит. Затем он вырезает эти биты из текущего значения $s$ или $h$, чтобы не обрабатывать их повторно. Правда, у этого алгоритма есть небольшая проблема: $k$ младших бит могут быть не обработаны. Для решения этой проблемы я использовал два подхода:


  • Для $s$ и $G$ и воспользовался тем, что кратные $G$ посчитаны заранее и зашиты в код. Я умножил значение $s$ на $2^k$, а кратные $G$ на $2^{-k}\bmod l$. Результат не меняется, но после умножения на $2^k$ младшие $k$ бит у $s$ гарантированно нулевые.
  • Для $h$ и $A$ этот способ не работает. Я рассматривал вариант умножить $h$ на $2^{-k}\bmod l$ и затем на $2^k$, но проблема в том, что $A$ является частью входных данных и не обязательно лежит в подгруппе размера $l$. Если $A$ не лежит в подгруппе размера $l$, то умножение на $(h2^{-k}\bmod l)\cdot2^k$ не эквивалентно умножению на $h$, а если вместо $l$ использовать $8l$ (полный размер группы), то по этому модулю нельзя вычислить $2^{-k}$. В итоге, я решил сделать так: вместо $h$ использовать $h+8l-2^k$ (то же самое, что $h-2^k$ по модулю $8l$), а в конце сделать ещё одно сложение: result := subtract(result, Amul[(1 << k) + h]), то есть дообработать то, что осталось от $k$ младших бит, при этом прибавка $2^k$ нужна, потому что кратные $A$ посчитаны только для коэффициентов от $2^k$ до $2^{k+1}-1$.

В этой реализации я использую значение $k=3$, которое обеспечивает баланс между затратами на предпосчёт, использованием памяти и экономией вычислений в основном цикле.


Следующий вопрос: как реализовать сложение, вычитание и удвоение точек. Обычные реализации в криптографических библиотеках оптимизированы в расчёте на то, что умножение по модулю $p$ намного дороже сложения. В EVM эти операции стоят почти одинаково, поэтому стоило подобрать формулу вручную. В этом очень помогает база данных формул для операций на эллиптических кривых Explicit-Formulas Database. Вот страница с формулами для того типа кривой, к которым относится Curve25519. Как видно, в качестве источника для всех формул указана одна и та же статья. В этой статье содержится важная информация, которой нет в EFD, в частности, о том, какие формулы являются полными, а какие нет. Неполные формулы представляют опасность: они будут работать на случайных тестах, но на специально подобранных входных данных они могут дать неправильный результат. В частности, формулы для сложения, у которых наименование заканчивается на -2 или -4 (см. список), являются неполными, поэтому их использовать не стоит (хотя они и более эффективные). В итоге я использовал в качестве основы формулу madd-2008-hwcd-3 для сложения и dbl-2008-hwcd для удвоения (там нет большого выбора). Однако, я внёс некоторые изменения.


Во-первых, я изменил используемые координаты. Там предлагается использовать так называемые расширенные проективные координаты (extended projective coordinates) такие числа $X$, $Y$, $Z$ и $T$, что $x=X/Z$, $y=Y/Z$ и $xy=T/Z$. Однако, в формуле для удвоения $T$ не используется, поэтому от его вычисления можно попробовать избавиться, если следующая операция удвоение. Для этого можно заметить, что как при сложении, так и при удвоении результат сначала вычисляется в виде координат $X$, $U$, $Y$ и $V$, таких что $x=X/U$ и $y=Y/V$, а затем эти координаты переводятся в обычные, для этого требуется четыре умножения (или три, если не вычислять $T$). Я решил хранить точки в виде $X$, $U$, $Y$, $V$, это позволяет сэкономить одно умножение при удвоении из-за отсутствия необходимости вычислять $T$.


Во-вторых, я продумал формат для предпосчитанных точек. Формулы madd реализуют так называемое смешанное сложение (mixed addition) сложение, при котором можно заранее посчитать часть промежуточных значений для одной из точек, чтобы уменьшить количество вычислений. Мне это очень удобно: у меня восемь точек посчитано ещё на этапе генерации кода и ещё восемь генерируется во время выполнения; переведя их в оптимальную для вычислений форму, можно сэкономить на сложениях в основном цикле, которых около ста. Я выбрал такое представление: $((y+x)/2, (y-x)/2, xyd)$. По сравнению с некоторыми более очевидными представлениями (например, $(y+x, y-x, xy\cdot2d)$, которое используется в libsodium) это позволяет сэкономить одно умножение на 2, которое в EVM стоит, как обычное умножение. В результате псевдокод для сложения выглядит так:


// Входные данные:// Точка 1: (x1, u1, y1, v1), x=x1/u1, y=y1/v1.// Точка 2: (s2, d2, t2), s2=(y+x)/2, d2=(y-x)/2, t2=x*y*d.// Выходные данные:// Точка 3: (x3, u3, y3, v3), x=x3/u3, y=y3/v3.// Точка (x4, y4, z4, t4) - то же самое, что и точка 1, но в других координатах.x4 := x1 * v1y4 := y1 * u1z4 := u1 * v1t4 := x1 * y1// Далее см. формулы madd-2008-hwcd-3.s4 := y4 + x4d4 := y4 - x4a := d4 * d2 // (y4-x4)*(y2-x2)/2, соответствует A/2.b := s4 * s2 // (y4+x4)*(y2+x2)/2, соответствует B/2.c := t4 * t2 // Соответствует C/2.             // D/2 - это просто z4, вычислять не надо.x3 := b - a  // E/2.u3 := z4 + c // G/2.y3 := b + a  // H/2.v3 := z4 - c // F/2.// Значения x3, u3, y3, v3 отличаются от E, G, H, F коэффициентом 1/2, но это не важно, потому что значения x3/u3 и y3/v3 те же самые.

Псевдокод для удвоения выглядит так:


// Входные данные:// Точка 1: (x1, u1, y1, v1), x=x1/u1, y=y1/v1.// Выходные данные:// Точка 2: (x2, u2, y2, v2), x=x2/u2, y=y2/v2.// Точка (x3, y3, z3) - то же самое, что и точка 1, но в других координатах. Значение t3 не нужно.x3 := x1 * v1y3 := y1 * u1z3 := u1 * v1// Далее см. формулы dbl-2008-hwcd.xx := x3 * x3 // A.yy := y3 * y3 // B.xy := x3 * y3 // А вот здесь выгоднее вычислить E как 2xy, а не так, как там.zz := z3 * z3x2 := xy + xy // E.u2 := yy - xx // G.y2 := yy + xx // -H.v2 := zz + zz - u2 // -F.// Опять же, коэффициент -1 у значений y2 и v2 не влияет на правильность результата.

Ещё одна оптимизация: для сложения не обязательно использовать инструкцию addmod (сложение по модулю). Если слагаемые меньше, чем $2^{255}$, то можно использовать обычное сложение (оно дешевле), так как результат гарантированно не переполнит 256-битный тип. Результат такого сложения, однако, можно использовать только в операциях, использующих значение по модулю. Например, если нужно сложить три числа, то из двух сложений по модулю можно заменить на обычное сложение только одно.


Проверка результата


Наконец, полученную точку нужно упаковать. Это несложно: посчитать $x=X/U$ и $y=Y/V$, затем записать младший бит $x$ на место старшего бита $y$. Поскольку нахождение обратного по модулю затратная операция (она реализована посредством возведения в степень $p-2$), используется оптимизация, позволяющая находить обратное только один раз: для этого вычисляется $(UV)^{-1}$, из него можно вычислить $U^{-1}=(UV)^{-1}V$ и $V^{-1}=(UV)^{-1}U$. Упакованная точка сравнивается со значением $R$, записанным в подписи. Если совпала, то подпись правильная. Вот и всё.


Заключение


К счастью, в NEAR нет нужды в подобных извращениях с кодом. Смарт-контракты можно писать на Rust и AssemblyScript (похож на TypeScript) с использованием существующих библиотек.


Посмотреть, как выглядит разработка под NEAR, и поэкспериментировать в онлайн-IDE можно здесь.


Следить за всеми новостями на русском можно в группе в Телеграме и в группе ВКонтакте, а на английском в официальном твиттере.


До скорых встреч!

Подробнее..

Из песочницы Разбираемся с форматами токенов на Ethereum

25.07.2020 22:11:28 | Автор: admin
Со временем блокчейн всё сильнее проникает в нашу жизнь, и появляется необходимость понимать основные его технологии, в том числе работу децентрализованных приложений (dApps). Большинство dApps в данный момент создано на Ethereum, возможности которого гораздо более гибкие, чем выпуск привычных ERC20 токенов.

Зачем нужны стандарты


Внутри смарт-контракта (что это?) можно прописать уникальную механику токена. Он будет работать, но другие разработчики (и смарт-контракты) не смогут обращаться к нему с помощью универсальной логики.

Пример: вы создали токен для игрового предмета (децентрализованный гейминг сейчас активно развивается) и хотите, чтобы он мог торговаться на маркетплейсах для игровых предметов (каком-нибудь таком). Но маркетплейсы не знают, как обращаться к функциям внутри вашего контракта, им нужен отдельный смарт-контракт, чтобы поддерживать ваш токен.

На помощь приходят стандарты. Зная способы взаимодействия с ними, создатели других dApps заложили в код всё необходимое для работы с токеном на опредёленном стандарте.

Как появляются стандарты


Ethereum является open-source проектом (кстати, ERC это Ethereum Request for Comments), поэтому логично, что новый стандарт токена может предложить любой пользователь. Если стандарт решает какую-то важную проблему, то он может стать официальным стандартом Ethereum (то есть попасть в этот список).

Взаимозаменяемые и не взаимозаменяемые токены


Отправной точкой для классификации стандартов токенов является их взаимозаменяемость или её отсутствие. Fungible (взаимозаменяемые) токены равны друг другу, их можно использовать в качестве валюты. Semi-fungible (на половину взамозаменяемые) токены почти неотличимы друг от друга, но всё-таки уникальны (пример: билеты в кинотеатре, стоимость может быть одна, но место у каждого точно уникальное). Non-fungible (не взаимозаменяемые) токены полностью уникальны, токенизированный объект в единственном экземпляре (пример: объекты авторского права).
image
Eсли не узнаёте котёнка, то это одна из первых игр на Ethereum (и стандарте ERC-721), CryptoKitties.

ERC-20


Самым известным стандартом взаимозаменяемых токенов является ERC20, который предложил автор идеи Ethereum Виталик Бутерин ещё в 2015. Этот токен широко используется для проведения разных типов initial offering (первое предложение). Я избегаю терминов ICO и IEO, потому что теперь это далеко не единственные способы провести публичное размещение токенов (но статья не об этом).

Специфика: взаимозаменяемый стандарт, есть тикер для бирж, делимый (количество знаков после запятой определяет создать смарт-контракта), обязательно имеет total supply (количество токенов, больше которого выпустить нельзя).

Про ERC-20 написано уже много (хабр), перехожу к другим стандартам.

ERC-721


Данный стандарт широко применяется для создания уникальных токенов. Земля в Decentraland, Binance Collectibles, вот примеры ERC-721.

ERC-721 был предложен как EIP (предложение по улучшению Ethereum) Дитером Ширли в 2017, стал официальным в 2018.

Специфика: не взаимозаменяемый стандарт, нет тикера, неделимый, обязательно существует только в единственном экземпляре.

ERC-721, как и ERC-20 широко распространен, поэтому не буду останавливаться на нём.

ERC-777


Этот формат является усовершенствованием привычного ERC-20. Он обратно совместим с ERC-20, но имеет несколько преимуществ:

  • при обмене токенов использует одну транзакцию вместо двух в ERC-20;
  • автоматически отменяет транзакции на несовместимые контракты;
  • возможность помечать неблагонадёжные адреса;
  • возможность назначать операторов (адреса, имеющие право отправлять токены с другого адреса);
  • ускоренное проведение транзакций.


ERC-223


Также является усовершенствованием ERC-20, предотвращая отправку транзакций на случайные контракты. Если смарт-контракт не имеет функций, предусматривающих работу с токенами, то они возвращаются отправителю.

картинка: mywishplatform

ERC-1155


Идею этого стандарта предложили разработчики проекта Enjin в 2018. Enjin проект, стремящийся к упрощению создания игр на Ethereum. Поэтому предложенный ими стандарт стоит сначала рассмотреть как игровой, хотя его применение этим не ограничивается.

Специфика:

  • позволяет выпускать несколько токенов в одном контракте;
  • токены в одном контракте могут быть fungible и non-fungible одновременно;
  • поддерживает атомарные свопы;
  • поддерживает batch транзакции;
  • не для всех транзакций нужно ждать окончания блока.

Если первые два пункта понятны, то третий и четвёртый могут вызвать вопросы. Для начала разберёмся с атомарными свопами.

Атомарные свопы


Одной из причин непринятия повсеместно блокчейна является невозможность быстро и эффективно (в плане комиссий) обменивать одни токены на другие (а количество различных токенов все время увеличивается). Решение проблемы уже создано это атомарные свопы. Обычно под Atomic swaps понимают технологию децентрализованного обмена между криптовалютами разных самостоятельных блокчейнов (об этом неплохо написано на BitcoinWiki). Но также стоит рассматривать атомарные свопы и в контексте обмена токенов внутри смарт-контракта.

Картинка из блога Enjin иллюстрирует своп множественных токенов на стандарте ERC-1155.
Фото из блога Enjin

А batch transactions хоть и не экономят время, зато экономят газ (что это?), записывая в сеть несколько транзакций, как одну.
Фото из блога Enjin

Стоит упомянуть, что хоть ERC-1155 получил большее распространение, он многое перенял от ERC875, появившегося несколькими месяцами ранее. ERC-875 предлагал тот же функционал, кроме поддержки fungible токенов.

ERC-865


Стандарт, аналогичный ERC-20, но использует для комиссий не газ, а сами токены. Из-за сложной системы оплаты комиссии газом (цена газа выбирается самостоятельно), а иногда и непредсказуемости размера комиссии, такое улучшение может быть очень полезно для принятия токенов на Ethereum.

Ссылки


Я рассмотрел далеко не все стандарты, но если говорить о всех ERC, то они по большей части похожи друг на друга, и предлагают или решение проблем ERC-20, или применение в какой-то отдельной нише. Если хотите подробно вчитаться в код: Github EIPs, Github OpenZeppelin. Ethereum.org.
Подробнее..

Hello Word смарт-контракт для TON (FreeTON)

08.03.2021 18:14:03 | Автор: admin

Одна из интересных тем, о которой мне хотелось бы сегодня рассказать - это программирование смарт-контрактов. За язык на котором мы будем программировать свой смарт возьмем Solidity, а в качестве платформы будем использовать FreeTON

В технологию blockchain сегодня не будем погружаться, ибо про него уже много статей. Поэтому рассмотрим простой смарт-контракт в следующем порядке:

  1. Как он устроен;

  2. С чего начать;

  3. "Hello World";

  4. Особенности TON смарт-контрактов;

  5. Ссылки на дополнительную информацию.

Как устроен смарт-контракт

По сути смарт-контракт это обычный файл, как небольшая компьютерная программа он состоит из исполняемого кода, данных программы и мета-данных. Чтобы его запустить вовсе необязательно нужен blockchain. Для простого "Hello World" или калькулятора достаточно только компилятора и программы, запускающей функции смарт-контракта (назовем ее run executor).
У скомпилированного смарт-контракта нет единой точки входа (функции main), вызывать можно любые public функции на свой вкус, для этого в комплекте с ним при компиляции генерируется ABI (благодаря которому внешняя вызывающая программа отыскивает нужную функцию, то есть точку входа, внутри откомпилированного бинарного образа смарт-контракта).
Поэтому мы можем представить себе его как микро-сервис, либо еще проще как веб-сервис в виде, скажем PHP-скрипта.
Как можно догадаться такой скрипт сам по себе не генерирует пользовательское UI или HTML-разметку, а следовательно, ему требуется Frontend.
Поэтому у подобных blockchain-проектов есть JavaScript-фреймворки, обеспечивающие такое взаимодействие (и конечно они все используют ABI смарт-контракта с которым хотят иметь дело).
Про взаимодействие и deploy в blockchain стоит написать отдельную статью.

Теперь взглянем на смарт-контракт с точки зрения программиста. Программисты думают о них как о классах ООП. Поэтому следует рассмотреть отличия классов и смарт-контрактов.

Хранение в памяти глобальных переменных (свойств) смарт-контрактов своеобразно тем, что они хранятся в blockchain как в базе данных, доступ к их значениям обеспечивается через вызов функций. Когда речь идет про обычный класс, то такие данные необходимо записывать на носитель постоянной памяти самостоятельно.

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

Внутри функций смарт-контрактов мы можем обращаться к окружению (переменным и свойствам, инициализированным из виртуальной машины, на основе данных текущей транзакции, блока и состояния всего blockchain.

Быстрый старт

Для работы нам понадобиться VSCode и плагин TONDev, установив его выполним следующее:

В области проводника VSCode кликнем правой кнопкой мыши, и в контекстном меню снизу выберем Create Solidity Contract:

Выбор пункта Create Solidity Contract в VSCodeВыбор пункта Create Solidity Contract в VSCode

Появится сгенерированный плагином файл Contract.sol:

Созданный смарт-контракт по умолчанию в VSCodeСозданный смарт-контракт по умолчанию в VSCode

Теперь мы его можем скомпилировать, кликнув по нему и в контекстном меню выбрав Compile Solidity Contract:

Компиляция смарт-контракта в VSCodeКомпиляция смарт-контракта в VSCode

Таким образом мы можем сразу же получить готовый, но не особо полезный смарт-контракт. В рабочую директорию проекта добавится скомпилированный .tvc и .abi.json.
Подсветка ошибок связана с особенностями, о которых поговорим далее, а пока давайте напишем свой собственный HelloWorld.sol.

Hello World!

В самом простом виде наш "Hello World" будет выглядеть так:

pragma ton-solidity >= 0.35.0;pragma AbiHeader expire;contract HelloWorld {    function HelloWorld() public pure returns (string) {        tvm.accept();        return 'Hello World!';    }}

Или можно написать внутри нашей функции вот так tvm.log("Hello World!"); это инструкция для виртуальной машины TON, поэтому давайте поговорим про TON Solidity Compiller API.

Особенности TON смарт-контрактов

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

Как видно из примера нашего "Hello World", первой инструкцией мы выставляем tvm.accept(); это и есть обращение к API виртуальной машины TON. Таким образом мы сообщаем смарт контракту, что необходимо выполнить данную функцию даже в том случае если аккаунт не профинансировал требуемый вызов функции, а функция будет вызвана за счет средств на балансе аккаунта смарт-контракта (как и пользовательские аккаунты, у смарт-контрактов имеется похожий счет).

Так как "газ" для смарт-контрактов служит средством защиты от спам-атак, он требует финансовых затрат, выраженных в криптовалюте blockchain-сети. Cледовательно, вызов tvm.accept(); расходующий средства со счета смарт-контракта не очень-то и выгоден с точки зрения предоставления услуг (в большинстве случаев). Чтобы сбалансировать расходы и определить есть ли необходимость выполнять смарт контракт за счет баланса аккаунта самого смарт-контракта или же возложить расходы на вызывающий аккаунт, можно воспользоваться инструкцией require().

Инструкция require() (требование) позволяет задать условия, только при выполнении которого начнется выполнение функции смарт-контракта. Например, мы можем перед вызовом tvm.accept(); добавить требование require(msg.pubkey()==tvm.pubkey()); которое не допустит вызов функции смарт-контракта, если публичный ключ отправителя смарт-контракта не соответствует ключу аккаунта самого смарт-контракта.

Пока я думаю для первой стати хватит. Давайте сделаем так. Если читателям данная тема будет интересна я буду писать тут статьи про смарт-контракты и взаимодействие с ними из JavaScript, а так же отвечать на вопросы. Буду рад любым замечаниям, поправкам и напутствиям. А пока расскажу, где можно найти информацию по данной теме самостоятельно.

Ссылки

Для получения основополагающей информации о проекте TON можно зайти на официальный сайт проекта Павла и Николая Дуровых. К сожалению из-за сложностей с регуляторами в США проект, как часть Telegram, закрыт. Проект который продолжил путь стал независимым сообществом. А тут находится документация для разработчиков. Ну и github.

Подробнее..

Основы программирования смарт-контрактов TON (FreeTON)

15.03.2021 22:22:10 | Автор: admin

Перед тем как мы начнем писать свой токен, а затем систему смарт-контрактов, давайте проведем краткий обзор важных элементов смарт-контрактов для FreeTON.

В предыдущей статье Hello Word смарт-контракт для TON (FreeTON) мы рассмотрели самое начало работы с смарт-контрактами FreeTON и HelloWorld.

Структура данной статьи:

  1. Типы чисел int и uint, pure-функции и event (события): напишем калькулятор на blockchain;

  2. Структуры и массивы: смарт-контракты как база данных;

  3. Хеш-таблицы: mapping;

  4. Публичные ключи и require.

Примечание: в коде далее в начале функций будет присутствовать вызов tvm.accept(); поскольку мы пока что рассматриваем учебные варианты смарт-контрактов.

Калькулятор

Объявить переменную с числовым типом можно с помощью int (целое число со знаком) и uint (целое без знака). Можно указать количество бит для числа в суффиксе указанного числового типа, например: int8, int16, uint128. Типы int и uint - это то же что и int256 и uint256. Над числами в смарт-контракте мы производим арифметические операции.

Теперь напишем смарт-контракт с калькулятором. Он имеет одну функцию calc, которая принимает 3 uint-параметра: 2 числа-операнда и число, обозначающее номер операции над переданными функции операндами. У нашей функции calc также присутствует модификатор pure, что говорит, о том, что данная функция не использует данные смарт-контракта, а выполняет свои операции только с тем, что ей передано в аргументах.

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

pragma ton-solidity >= 0.35.0;pragma AbiHeader expire;contract HelloCalc {    event UnknownOperator(uint op);    function calc(uint a, uint b, uint op) public pure returns (uint) {        tvm.accept();                if(op == 1) {            return a + b;        } else if(op == 2) {            return a - b;        } else if(op == 3) {            return a * b;        } else if(op == 4) {            return a / b;        } else if(op == 5) {            return a % b;        } else if(op == 6) {            return a ** b;        }else {            emit UnknownOperator(op);            return 0;        }    }}

События (event) в смарт-контрактах используются весьма часто, а тут оно нам пригодилось, для того, чтобы сообщить дополнительную информацию, о том, что возвращаемый функцией результат 0 это не результат, а ошибка связанная с тем, что передан неизвестный номер арифметического оператора.

Структуры и массивы

То что мы объявляем в начале (как правило) смарт-контракта, вне области функций, относится к области памяти, связанной со смарт-контрактом. А так, как смарт-контракт в итоге становится загружен в blockchain, то данные смарт-контракта хранятся в blockchain как в базе данных. Напишем простой смарт-контракт для хранения данных в blockchain.

pragma ton-solidity >= 0.35.0;pragma AbiHeader expire;contract HelloUser {    event NewUser(uint id);        struct User {        uint weight;        uint balance;    }    User[] users;    function AddUser(uint w, uint b) public {        tvm.accept();        users.push(User(w, b));        emit NewUser(users.length - 1);    }    function GetUser(uint id) public view returns (uint w, uint b) {        tvm.accept();        w = users[id].weight;        b = users[id].balance;    }}

Структура User в нашем контракте служит для хранения информации о пользователе, и содержит 2 поля данных типа uint: вес и баланс юзера. Поскольку мы хотим хранить данные не об одном, а о многих юзерах, после объявления структуры, мы объявили массив структур этого типа. Теперь мы можем добавлять в наш массив пользователей с помощью push, а затем вычислив по индексу массива идентификатор нового юзера - возвращаем его в событии NewUser.

Обратите внимание на то как выглядит возврат значений функцией GetUser: в данном случае мы объявляем переменные в returns, которым в теле функции присваиваем значения. Также заметьте модификатор pure мы поменяли на view.

Хеш-таблицы: mapping

Выше мы рассмотрели массивы, у которых ключом к значению (User) является индекс (или порядковый номер, начиная с нуля) элемента в нём. Другой разновидностью коллекций элементов являются отображения mapping. В них также мы можем хранить последовательности элементов заданного типа, как и в случае с массивами, но в отличии от массивов, ключами к таким значениям являются не простые числовые индексы, а значения другого заданного типа. Это позволяет производить поиск элементов в отражении по строке, адресу, публичному ключу и другим значениям.

Давайте добавим такой mapping в наш немного переделанный пример:

contract HelloToken {    event TokenCreated(uint owner, uint tid);        struct Token {        string name;        string symbol;    }    Token[] tokens;    mapping (uint => uint) accounts;    function NewToken() public {        tvm.accept();        tokens.push(Token("", ""));        accounts[tokens.length-1] = msg.pubkey();        emit TokenCreated(msg.pubkey(), tokens.length-1);    }   function GetTokenOwner(uint tid) public view returns (uint) {       tvm.accept();       return accounts[tid];   }   function GetTokenInfo(uint tid) public view returns (string _name, string _symbol) {       tvm.accept();       _name = tokens[tid].name;       _symbol = tokens[tid].symbol;   } function SetTokenInfo(uint tid, string _name, string _symbol) public {       require(msg.pubkey() == accounts[tid], 101);       tvm.accept();       tokens[tid].name = _name;       tokens[tid].symbol = _symbol;   }}

Публичные ключи и require

Вызов функции смарт-контракта похож на отправку сообщения, в том плане, что мы отправляем на адрес аккаунта со смарт-контрактом объект, содержащий название вызываемой функции и параметры для нее (если они требуются). Помимо этого в сообщение может быть добавлены дополнительные данные: публичный ключ, подпись, значение value для передачи токенов TON Crystal в вызываемый аккаунт. Поговорим о публичном ключе. Получить значение публичного ключа аккаунта отправившего сообщение нашему смарт-контракту можно путем вызова API функции msg.pubkey(). В функции NewToken() мы выполнили присваивание accounts[tokens.length-1] = msg.pubkey(); в котором для каждого вновь создаваемого ID токена в отображение accounts помещается публичный ключ создавшего этот токен аккаунта, а в качестве ключа мы используем идентификатор этого токена, который вычисляем после пополнения массива tokens. Затем в SetTokenInfo() где мы можем настроить название и символ для нашего токена, мы задаем требование, только при выполнении которого возможно продолжение вызова функции. Мы берем публичный ключ отправителя по значению tid находим в отображении accounts установленный при создании публичный ключ и сравниваем. Только если они равны мы сможем установить или изменить имя и символ токена.

Тут были описаны аспекты, для читателей, изучающих тему смарт-контрактов FreeTON "с нуля", а в следующих статьях мы начнем создавать собственный токен.

Подробнее..

Из песочницы Ethereum Python Brownie

05.07.2020 12:20:18 | Автор: admin

Салют, дорогой криптоэнтузиаст!


Сегодня речь пойдёт о Brownie аналоге фреймворка Truffle, который часто используется для разработки умных контрактов на Solidity, их тестирования и развёртывания (о чём можно почитать в цикле соответствующих статей здесь).


Так зачем же нужен ещё один фреймворк и в чём его ключевое отличие от Truffle?


  • Во-первых, в них используются разные языки в то время, как Truffle опирается на JS, не все знают этот язык и не всем его комфортно использовать; в brownie же в используется Python 3.
  • Во-вторых, brownie за счёт интеграций различного софта делает разработку удобнее: тут и менеджер пакетов ethpm для умных контрактов, и ganache для развёртывания локальной тестовой цепочки, и тесты через pytest, и все версии компиляторов solc, и даже биндинги к MythX инструменту для автоматического поиска ошибок в умных контрактах,- иначе говоря brownie предлагает инструменты для всего цикла разработки. Конечно Truffle тоже позволяет использовать все эти инструменты, однако они не встроены во фреймворк и их приходится устанавливать дополнительно.
  • В-третьих, brownie позволяет работать не только с умными контрактами на Solidity, но и на vyper типизированном python-based для разработки умных контрактов.

Таким образом, если вы предпочитаете работать с пайтоном и хотите упростить себе разработку умных контрактов, то однозначно стоит попробовать brownie.


Что же ещё умеет brownie?
Как сказано в самом репозитории brownie это фреймворк разработки полного цикла умных контрактов для Ethereum-based платформ, поддерживающий:


  • Несколько языков программирования умных контрактов: Solidity и Vyper.
  • Сборку контрактов.
  • Интерактивное взаимодействие с контрактами.
  • Тестирование умных контрактов с помощью pytest.
  • Скрипты для взаимодействия с умными контрактами.
  • Работу с шаблонами умных контрактов.

Рассмотрим все эти возможности и организацию проекта на brownie подробнее, для чего установим brownie себе на машину. Сделать это проще всего можно с помощью pip:


pip install eth-brownie


Теперь brownie доступен как консольная утилита.


help для brownie
brownie --helpBrownie v1.6.9 - Python development framework for EthereumUsage:  brownie <command> [<args>...] [options <args>]Commands:  init               Initialize a new brownie project  bake               Initialize from a brownie-mix template  ethpm              Commands related to the ethPM package manager  compile            Compiles the contract source files  console            Load the console  test               Run test cases in the tests/ folder  run                Run a script in the scripts/ folder  accounts           Manage local accounts  gui                Load the GUI to view opcodes and test coverage  analyze            Find security vulnerabilities using the MythX APIOptions:  --help -h          Display this messageType 'brownie <command> --help' for specific options and more information abouteach command.

Проект brownie


Проект brownie представляет из себя определённую структуру директорий и конфигурационный файл brownie-config.yaml. Создать проект можно либо с помощью команды brownie init


Создание проекта с помощью init
brownie init salut_habrBrownie v1.6.9 - Python development framework for EthereumSUCCESS: Brownie environment has been initiated at salut_habr

либо можно создать проект на основе шаблона с помощью команды brownie bake template_name


Создание проекта из шаблона
brownie bake tokenBrownie v1.6.9 - Python development framework for EthereumDownloading from https://github.com/brownie-mix/token-mix/archive/master.zip...5.62kiB [00:00, 2.82MiB/s]SUCCESS: Brownie mix 'token' has been initiated at token

Далее я рассмотрю второй вариант на основе шаблона для ERC-20 токена (шаблон token).
Рассмотрим структуру проекта:


build # Здесь хранится информация, полученная в результате сборки и деплоя.   contracts # Здесь хранятся скомпилированные контракты, их ABI и метаданные.   deployments # Здесь хранятся данные о раздеплоенных в сети умных контрактах.contracts # Сами контракты (код и библиотеки).interfaces # Интерфейсы умных контрактов.reports # Отчёты анализаторов.scripts # Python скрипты с доступом к окружению проекта и возможностью вызова через run.tests # Тесты на базе pytest для умных контрактов.

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


Команды brownie


brownie даже в базовой комплектации имеет множество команд, но я рассмотрю четыре из них, которые значительно чаще прочих используются в производственном цикле: compile, console, test и run.


brownie compile


Данная команда используется для компиляции умных контрактов, которые расположены в директории проекта contracts или её поддиректориях. Если необходимо, чтобы часть контрактов не компилировалась как самостоятельные единицы, то к названию файла или директории стоит приписать слева символ нижнего подчёркивания "_",- в таком случае компилятор brownie будет их игнорировать (это полезно при подключении библиотек к проекту).


Собранные контракты помещаются в ./build/contracts/ в виде одноимённых json-файлов, которые содержат ABI контрактов, их байт-код и дополнительную мета-информацию.


При компиляции brownie запоминает, какие контракты были скомпилированы, а какие ещё нет и компилирует только их. Но если нужно перекомпилировать все контракты, то можно сделать это добавив флаг -all.


Пример компиляции контрактов
brownie compile --allBrownie v1.6.9 - Python development framework for EthereumCompiling contracts...  Solc version: 0.5.17+commit.d19bba13.Windows.msvc  Optimizer: Enabled  Runs: 200  EVM Version: IstanbulGenerating build data... - Token... - SafeMath...Brownie project has been compiled at C:\Users\Default\Documents\token\build\contracts

Параметры компиляции, такие как версия компилятора или оптимизация кода, задаются в файле brownie-config.yaml


brownie test


Этой командой запускаются тесты в проекте с использованием pytest, однако стоит заметить, что команда не возвращает никакое значение, поэтому есть трудности с интеграцией тестов в CI/CD.


Пример тестирования контрактов
brownie testBrownie v1.6.9 - Python development framework for Ethereum==================================================================================================== test session starts =====================================================================================================platform win32 -- Python 3.8.3, pytest-5.4.1, py-1.8.1, pluggy-0.13.1rootdir: C:\Users\Default\Documents\tokenplugins: eth-brownie-1.6.9, hypothesis-5.5.4, forked-1.1.3, xdist-1.31.0, web3-5.5.1collecting ... Launching 'ganache-cli.cmd --port 8545 --gasLimit 6721975 --accounts 10 --hardfork istanbul --mnemonic brownie'...collected 7 itemstests\test_approve_transferFrom.py ......                                                                                                                                                                               [ 85%]tests\test_transfer.py .                                                                                                                                                                                                [100%]===================================================================================================== 7 passed in 9.35s ====================================================================================================== Terminating local RPC client...

Тесты в проекте хранятся в директории tests/


brownie run


С помощью данной команды осуществляется запуск скриптов из директории scripts. Однако передача параметров в них поддерживается только с версии brownie 2.x, но даже без них удобно использовать данный функционал для интеграции с CI/CD (например для деплоя контрактов).


Деплой контрактов с помощью brownie run
brownie run tokenBrownie v1.6.9 - Python development framework for EthereumTokenProject is the active project.Launching 'ganache-cli.cmd --port 8545 --gasLimit 6721975 --accounts 10 --hardfork istanbul --mnemonic brownie'...Running 'scripts.token.main'...Transaction sent: 0xe36fbf7d93c1c91bde5e9290128999ed06ea54eb68352fb477fa91ce8072f472  Gas price: 0.0 gwei   Gas limit: 549953  Token.constructor confirmed - Block: 1   Gas used: 549953 (100.00%)  Token deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87Terminating local RPC client...

brownie console


Запускает интерактивный режим brownie: по сути он является интерпретатором установленной версии питона, но с заранее импортированными пространствами имён для проекта. Например для проекта токена мы имеем следующие переменные сразу после запуска:


brownie consoleBrownie v1.6.9 - Python development framework for EthereumTokenProject is the active project.Launching 'ganache-cli.cmd --port 8545 --gasLimit 6721975 --accounts 10 --hardfork istanbul --mnemonic brownie'...Brownie environment is ready.>>> dir()[Fixed, Gui, SafeMath, Token, Wei, a, accounts, alert, compile_source, config, dir, history, network, project, rpc, run, web3]

Информацию о их назначении можно найти в документации brownie, однако подавляющая часть имён имеет то же предназначение, что и в web3py.


Работа с майнетом/тестнетами


Во всех примерах выше brownie поднимал ganache (локальное тестовое окружение Ethereum) и работал с ним, однако есть возможность работы с произвольной выбранной сетью (в том числе с приватным тестнетом и даже с Quorum!). Для этого используется параметр --network network_name при выполнении команд console и run, где network_name должна быть описана в brownie-config.yaml. По умолчанию там уже заданы майнеты ETH и ETC, а также локальный и публичные тестнеты. Однако можно добавлять свои сети в том числе добавляя в их свойства свои собственные параметры, если yaml позволяет это сделать. Обычно я при работе создаю дополнительно сети develop, test и master (по названиям веток в гите), а их блокчейны разворачиваю в Azure с помощью специальной службы.


Подводя итог можно сказать, что brownie на текущий момент уже достаточно зрелое Enterpise-ready решение для разработки под Ethereum и способен удовлетворить практически все возникающие в её процессе потребности. Питонистам и не только однозначно стоит попробовать сделать на нём свой следующий проект.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru