Обновляемые смарт-контракты

Если эмитент контракта хочет иметь способ обновить код контракта, чтобы данные учетной записи и другие данные переносились, может ли Ethereum предоставить это? Также можно ли это сделать без изменения адреса контракта или всегда нужно развертывать новый контракт?

Существуют ли механизмы «приложений» для добавления новых функций в контракт без его полной перезаписи?

Это может быть полезно для потенциальных читателей этого интересного вопроса: OpenZeppelin только что добавила функцию обновления в свой мастер смарт-контрактов , упрощая создание обновляемых токенов ERC721, ERC20 и ERC1155 с помощью UUPS или прозрачного шаблона.
Если кто-то, как я, более визуален, то это короткое видео было полезно, когда я обновлял свой контракт Ethereum — youtube.com/watch?v=sQJ-XQBzEuc.

Ответы (13)

Да. Существует ряд подходов, с помощью которых вы можете обновить Contract1до Contract2, сохраняя его состояние (данные и баланс) с тем же адресом, что и раньше.

Как это работает? Один из способов — использовать прокси-контракт с fallbackфункцией, в которой каждый вызов метода/trx делегируется контракту реализации (который содержит всю логику).введите описание изображения здесь

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

При таком подходе пользователи взаимодействуют только с прокси-контрактом, и мы можем изменить контракт реализации, сохранив тот же прокси-контракт.

введите описание изображения здесь

Функция fallbackбудет выполняться при любом запросе, перенаправляя запрос в реализацию и возвращая результирующее значение (используя коды операций).

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

Как я могу написать обновляемые смарт-контракты?

OpenZeppelin предоставляет отличные инструменты CLI и JS-библиотеки , которые заботятся обо всех вышеперечисленных сложных proxyконтрактах, связывая их с контрактами реализации (логики) и управляя всеми контрактами, которые вы развертываете, используя CLI для возможности обновления, «из коробки».

Единственное, что вам нужно сделать, это написать свои контракты и использовать OpenZeppelin CLI или библиотеки для развертывания контрактов.

ПРИМЕЧАНИЕ. Есть несколько ограничений , о которых вам следует знать, в отношении того, как вам нужно писать свои контракты и как вы должны их обновлять. Также в этом посте есть ряд обходных путей этих ограничений .

Что в этом шаблоне останавливает злонамеренную приманку и подмену? Т.е. вы покупаете свои токены, доверяя балансу, потом разработчик переходит на новый контракт, где у вас забирают токены.
Во-первых, не покупайте токены, если вы не доверяете разработчику единственное правильное обновление. В хорошем проекте DAO прокси-ключи обновления будут контролироваться мультиподписью или прямым голосованием DAO. Но если вы беспокоитесь о злонамеренной приманке и переключении, вам, вероятно, не следует доверять проекту в первую очередь, поскольку есть тысячи других способов вас надуть.
Я также хотел бы уточнить, что вы можете легко развернуть прокси-контракты обновления самостоятельно — вы можете найти пример здесь: github.com/Dawn-Protocol/dawn-erc20-erc777 .
Существуют ли прокси-контракт и контракт на реализацию в цепочке с отдельными адресами? Если контракт обновляется, могут ли люди по-прежнему вызывать старый контракт напрямую, минуя прокси?

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

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

Другой метод — поместить ваш логический код в библиотеку, а затем использовать функцию CALLCODE через библиотеки в Solidity для вызова кода, расположенного по указанному обновляемому адресу. Таким образом, пользовательские данные сохраняются между версиями. Это имеет ограничение, заключающееся в том, что ABI логического контракта должен оставаться неизменным с течением времени.

Вот старый принцип, который я использовал для демонстрации разделения данных/кода некоторое время назад.

Усадьба Править:

Начиная с выпуска Homestead, теперь есть DELEGATECALLкод операции. Это позволяет по сути переадресовывать звонки на отдельный контракт с сохранением msg.senderи всего хранилища.

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

contract Relay {
    address public currentVersion;
    address public owner;

    function Relay(address initAddr){
        currentVersion = initAddr;
        owner = msg.sender;
    }

    function update(address newAddress){
        if(msg.sender != owner) throw;
        currentVersion = newAddress;
    }

    function(){
        if(!currentVersion.delegatecall(msg.data)) throw;
    }
}
Возможно, стоит упомянуть также преобразователи имен.
Вот отличный пример полной реализации этой идеи: gist.github.com/Arachnid/4ca9da48d51e23e5cfe0f0e14dd6318f .
Это очень умно и является формой внедрения зависимостей: входной контакт зависит от адреса текущей версии.
Эй, Тьяден Хесс, Джосси Кальдерон! Я немного смущен тем, как на самом деле реализовать эту концепцию. Я сделал суть на простом примере с одним пожертвованием контракта, которое необходимо обновить. Кто-нибудь будет достаточно любезен, чтобы взглянуть на это и сказать мне, если я полностью неправильно понимаю концепцию? gist.github.com/fabdarice/d513d620d9355312d085c7a68e6c6118 Очень ценю это, спасибо!
Эй, Тьяден Хесс, хороший пример. Как я могу работать с этим контрактом через web3. Предположим, у меня есть контракт с именем контракта DemoVersion1.sol, который содержит следующий код contract DemoVersion1 { function checkVersion() returns (uint){ return 1; } }, и мне нужно обновить контракт до второй версии, которая содержит следующий код contract DemoVersion2 { function checkVersion() returns (uint){ return 2; } }, как я могу обрабатывать вызов методов контракта, может ли кто-нибудь объяснить или указать на подходящий пример.
@fabdarice Ты понял это? Можете ли вы опубликовать еще одну рабочую суть?
@fabdarice Итак, размер данных, который функция должна знать заранее? Что если я верну динамический массив?
Правильно, вам нужно знать размер данных. Насколько мне известно, в Solidity не рекомендуется возвращать динамический массив => геттер с параметрами индекса.
Считаете ли вы, что существует проблема доверия с «прокси-контрактами»? Разработчикам приятно, что их смарт-контракт «обновляется», но это на самом деле означает, что существует чистый лист в отношении изменений, которые эти разработчики могут внести в существующий смарт-контракт, верно? Не происходит ли некоторое снижение доверия к контракту из-за его неизменяемости?
Да, это проблема, которую необходимо решать для каждого варианта использования. Часто хорошей идеей является предоставление механизма «согласия», который позволяет пользователям переходить на новый контракт по своему усмотрению. Другой способ — просто использовать преобразователь имен и модель TOFU.
Вопрос, на который мне лень проверять себя в данный момент. Разве использование реле не делает модификатор «просмотр» бесполезным? Плата за газ не взимается, если только это не предусмотрено другими контрактами, как это и есть. Или я ошибаюсь?
Я ничего не вижу в делегатском вызове по существу: с
Поскольку теперь доступна более точная общедоступная информация о возможности обновления, я перенесу галочку с правильным ответом на более подробный ответ.
Можем ли мы добавить новые функции в обновленный контракт? @Тджаден Хесс♦
В вашем основном коде я выпустил это, разделив хранилище и логику на два разных контракта; каждый вызов из логического контракта в контракт, в котором хранилище требует дополнительных 2000 газа, есть ли способ предотвратить эти дополнительные расходы на газ? @TjadenHess
Этот смысл довольно устарел, теперь вы должны использовать вызовы делегатов через библиотечные контракты. Но в целом всегда будут накладные расходы из-за дополнительного уровня косвенности. Переключение контекстов несколько затратно, поскольку узлам потенциально приходится извлекать каждое состояние контракта с диска.

Один из методов заключается в использовании системы контрактов, как описано ниже:

  1. Контракт "Регистрация" - будет содержать пары "имя - адрес" для всех контрактов вашей системы;
  2. Контракт Backend;
  3. Контракт Frontendс использованием Backend;
  4. Разверните Registerи получите его адрес;
  5. Развернуть Backendи зарегистрировать адрес Backendуже развернутого Register;
  6. Жестко закодируйте адрес Registerв источник Backend. Перед любым звонком Backendот Frontendвас следует позвонить своему Registerи узнать реальный адрес Backend.

Затем вы можете обновить свой Backendконтракт в любое время — просто разверните новый и перерегистрируйте его в Register.

Вызов внешнего контракта: solidity.readthedocs.org...

Также смотрите обсуждение на форуме: forum.ethereum.org...


UPD: Тот же, но более эффективный способ (возможно)

Первое развертывание:

  1. Напишите контракт Register, который может развертывать другие контракты с самим адресом в качестве аргумента конструктора;
  2. Напишите все остальные контракты - "обновляемые" контракты с конструкторами, требующими Registerадрес ;
    • возможно, контракты должны быть отключены или иметь метод уменьшения
  3. Разверните Register, предоставив своему конструктору данные - все остальные контракты с шага 2.

Обновление:

  1. Разверните новую версию «обновляемого» контракта с тем же адресом Register;
    • Или, может быть, если вы Registerможете развернуть другие контракты - дайте ему это
  2. (необязательно) отключить/убить старую версию «обновляемого» контракта;
  3. Зарегистрируйте адрес новой версии «обновляемого» контракта в файле Register.
Большой! Если у кого-то есть существующие закодированные примеры, пожалуйста, добавьте сюда :)
Я перемещаю отметку о правильном ответе, потому что другой ответ содержит обновленную информацию о том, как заключить контракт на ретрансляцию в Homestead .
Вопрос: В случае нескольких контрактов, не будет ли эффективнее сначала зарегистрировать все контракты, сохранить все их адреса, а затем развернуть контракт Register (передав все сохраненные адреса в качестве аргументов конструктора)? Может ли таким образом сэкономить много вызовов функций и соответствующего газа?
@BharatMallapur, да, конечно

Код контракта неизменяем, хранилище изменяемо, но вы не можете выполнить код, помещенный в хранилище, по крайней мере, пока.

Исправления в контрактах

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

Хранение

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

Доступ к хранилищу после самоуничтожения

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

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

Повторное развертывание по тому же адресу

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

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

Контракты, развернутые в блокчейне, неизменны, поэтому это означает:

  • адрес и код развернутого контракта не могут быть изменены
  • развертывание более нового (или даже идентичного) контракта создаст новый адрес
  • код нельзя добавить в развернутый контракт

Если проблемы с контрактом хотят иметь способ обновить код контракта, чтобы данные учетной записи и другие вещи переносились, какие средства Ethereum предоставляет для этого?

Простой способ расширить контракт C1 — убедиться, что у C1 есть функции/аксессоры, которые возвращают все имеющиеся у него данные. Можно написать новый контракт C2, который вызывает функции C1 и выполняет дополнительную или исправленную логику. (Обратите внимание, что если у C1 и C2 есть foo, где foo C1 ошибочен, а foo C2 исправлен, нет способа отключить вызов C1 foo.)

Можно использовать реестр, как описано в ответе @Alexander, чтобы другие DApps и контракты запрашивали в реестре адрес contractC, чтобы при «замене» C1 на C2 код DApp не требовалось изменять. Использование реестра таким образом предотвращает жесткое кодирование адреса C1 (чтобы C2, C3, C4 могли занять его место при необходимости), но DApp необходимо жестко закодировать адрес реестра.


РЕДАКТИРОВАТЬ: ENS, служба имен Ethereum, была только что развернута в тестовой сети (Ropsten).

См. вики ENS для быстрого старта и других подробностей. Вот введение:

ENS — это служба имен Ethereum, распределенная, расширяемая система имен, основанная на блокчейне Ethereum.

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

ENS развернут в тестовой сети Ropsten по адресу 0x112234455c3a32fd11230c42e7bccd4a84e02010.

Первоначальное обсуждение здесь .

И в реестре, как и в любой системе косвенного доступа, есть свои баги, проблемы с безопасностью и проблемы. Если вы хотите обновить контракт из-за угрозы безопасности, об этом следует помнить. Если бы в DAO была система апгрейдов, несомненно, ее использовали бы для взлома...
@bortzmeyer Согласитесь, любой механизм обновления имеет риски, которые могут быть использованы сами по себе, и эти риски следует учитывать.

Ответ, получивший наибольшее количество голосов, - это использовать, delegatecallи его очень сложно понять правильно.

https://blog.trailofbits.com/2018/09/05/contract-upgrade-anti-patterns описывает некоторые методы обновления, а также критические соображения, чтобы вы не вводили больше ошибок или ошибочный метод обновления, который не т работать.

Рекомендации по шаблону прокси

Перед вызовом delegatecall проверьте существование целевого контракта. Solidity не будет выполнять эту проверку от вашего имени. Игнорирование проверки может привести к непреднамеренному поведению и проблемам безопасности. Вы несете ответственность за эти проверки, если полагаетесь на низкоуровневую функциональность.

Если вы используете шаблон прокси, вы должны:

Иметь детальное представление о внутреннем устройстве Ethereum , включая точную механику вызова делегата и детальное знание внутреннего устройства Solidity и EVM.

Тщательно продумайте порядок наследования , так как он влияет на структуру памяти.

Внимательно рассмотрите порядок объявления переменных. Например, затенение переменных или даже изменение типа (как указано ниже) могут повлиять на намерения программиста при взаимодействии с делегатом.

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

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

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

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

Спасибо за внимание. Поскольку это самый последний и самый информативный ответ, хотя ответ только по ссылке, я отмечу его как правильный, потому что аудитория должна подробно понимать все аспекты возможности обновления.
Можем ли мы добавить новые функции в обновленный контракт? @эт
@alper Более простой способ, чем delegatecallиспользовать ENS: ethereum.stackexchange.com/questions/77520/…
Я перемещаю маркер правильного ответа, так как OpenZeppelin теперь предоставляет обширную документацию и инструменты для этого.
@MikkoOhtamaa Нет проблем. Плакат ответа, который вы приняли, также опубликовал его как ответ на пару других вопросов.

У @Nick Johnson есть базовый контракт для обновляемых контрактов.

По его словам , перед использованием нужно «полностью понять ограничения и недостатки».

/**
 * Base contract that all upgradeable contracts should use.
 * 
 * Contracts implementing this interface are all called using delegatecall from
 * a dispatcher. As a result, the _sizes and _dest variables are shared with the
 * dispatcher contract, which allows the called contract to update these at will.
 * 
 * _sizes is a map of function signatures to return value sizes. Due to EVM
 * limitations, these need to be populated by the target contract, so the
 * dispatcher knows how many bytes of data to return from called functions.
 * Unfortunately, this makes variable-length return values impossible.
 * 
 * _dest is the address of the contract currently implementing all the
 * functionality of the composite contract. Contracts should update this by
 * calling the internal function `replace`, which updates _dest and calls
 * `initialize()` on the new contract.
 * 
 * When upgrading a contract, restrictions on permissible changes to the set of
 * storage variables must be observed. New variables may be added, but existing
 * ones may not be deleted or replaced. Changing variable names is acceptable.
 * Structs in arrays may not be modified, but structs in maps can be, following
 * the same rules described above.
 */
contract Upgradeable {
    mapping(bytes4=>uint32) _sizes;
    address _dest;

    /**
     * This function is called using delegatecall from the dispatcher when the
     * target contract is first initialized. It should use this opportunity to
     * insert any return data sizes in _sizes, and perform any other upgrades
     * necessary to change over from the old contract implementation (if any).
     * 
     * Implementers of this function should either perform strictly harmless,
     * idempotent operations like setting return sizes, or use some form of
     * access control, to prevent outside callers.
     */
    function initialize();

    /**
     * Performs a handover to a new implementing contract.
     */
    function replace(address target) internal {
        _dest = target;
        target.delegatecall(bytes4(sha3("initialize()")));
    }
}

/**
 * The dispatcher is a minimal 'shim' that dispatches calls to a targeted
 * contract. Calls are made using 'delegatecall', meaning all storage and value
 * is kept on the dispatcher. As a result, when the target is updated, the new
 * contract inherits all the stored data and value from the old contract.
 */
contract Dispatcher is Upgradeable {
    function Dispatcher(address target) {
        replace(target);
    }

    function initialize() {
        // Should only be called by on target contracts, not on the dispatcher
        throw;
    }

    function() {
        bytes4 sig;
        assembly { sig := calldataload(0) }
        var len = _sizes[sig];
        var target = _dest;

        assembly {
            // return _dest.delegatecall(msg.data)
            calldatacopy(0x0, 0x0, calldatasize)
            delegatecall(sub(gas, 10000), target, 0x0, calldatasize, 0, len)
            return(0, len)
        }
    }
}

contract Example is Upgradeable {
    uint _value;

    function initialize() {
        _sizes[bytes4(sha3("getUint()"))] = 32;
    }

    function getUint() returns (uint) {
        return _value;
    }

    function setUint(uint value) {
        _value = value;
    }
}
Это мое любимое решение. ответ должен получить больше голосов! С последней вилкой также нет необходимости указывать размеры возвращаемых значений ( _sizes[bytes4(sha3("getUint()"))] = 32).
Я освещал эту тему здесь: youtube.com/watch?v=KBqDYF5jw-0

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

НО , у вас все еще могут быть обновляемые смарт-контракты, если вы примете во внимание следующее:

Это нужно планировать с самого начала. Ключевым моментом является номер 4. Но все остальные необходимы для реального и плавного обновления смарт-контракта.

Итак, вам нужно будет разработать свой смарт-контракт с учетом следующих 5 пунктов:

  1. Держите свои смарт-контракты модульными и отделяйте правила и логику от структуры данных. Поэтому, если вам нужно будет что-то изменить, вы измените только соответствующий контракт, и вам не нужно будет изменять многие или все контракты.
  2. Вы должны быть готовы к аварийному останову или автоматическому выключателю , чтобы иметь возможность остановить все операции во время любой миграции. Потому что вы не хотите оказаться в ситуации, когда люди все еще могут обновлять/вставлять данные в старую версию смарт-контракта во время миграции и после нее.
  3. Вы должны предварительно предоставить возможность считывать все данные из вашего смарт-контракта . Конечно, вы можете выполнить разрешенное чтение, ограничив чтение всех данных владельцем или любым другим доверенным пользователем или даже другим смарт-контрактом. Вам нужно будет прочитать старую версию вашего смарт-контракта и вставить в новую версию.
  4. Вы будете использовать одну из следующих стратегий для связи со своим смарт-контрактом. Я скопировал их из Smart Contact Best Practices :

Обновление нарушенных контрактов

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

...

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

Пример 1. Использование контракта реестра для хранения последней версии контракта

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

contract SomeRegister {
    address backendContract;
    address[] previousBackends;
    address owner;

    function SomeRegister() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner)
        _;
    }

    function changeBackend(address newBackend) public
    onlyOwner()
    returns (bool)
    {
        if(newBackend != backendContract) {
            previousBackends.push(backendContract);
            backendContract = newBackend;
            return true;
        }

        return false;
    }
}

У этого подхода есть два основных недостатка:

  1. Пользователи всегда должны искать текущий адрес, и любой, кто этого не делает, рискует использовать старую версию контракта.

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

Альтернативный подход заключается в том, чтобы контракт перенаправлял вызовы и данные в последнюю версию контракта:

Пример 2: Используйте DELEGATECALL для переадресации данных и вызовов

contract Relay {
    address public currentVersion;
    address public owner;

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function Relay(address initAddr) {
        currentVersion = initAddr;
        owner = msg.sender; // this owner may be another contract with multisig, not a single contract owner
    }

    function changeContract(address newVersion) public
    onlyOwner()
    {
        currentVersion = newVersion;
    }

    function() {
        require(currentVersion.delegatecall(msg.data));
    }
}

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

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

Тем не менее, я также рекомендую проверить прокси-библиотеки в Solidity , опубликованные Zeppelin Solutions и Aragon. По этому вопросу планируется разработать отраслевой стандарт.

  1. Вы должны иметь хорошую отработку стратегии и тактики . Потому что стоимость обновления вашего смарт-контракта может действительно разрушить вашу жизнь.

Для этого я создал статью на Medium под заголовком « Основные соображения по дизайну для Ethereum dApps (1): обновляемые смарт-контракты» и предоставил образец для каждого пункта из 5 вышеперечисленных.

Мы (я и моя команда) недавно работали над проблемой обновляемых контрактов после того, как сослались на сообщение colony.io об обновляемых контрактах . Итак, мы пришли к решению, в котором у нас есть разные уровни контракта, а не один контракт.

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

Посмотрите на этот контракт хранилища данных, чтобы лучше понять его - https://goo.gl/aLmvJ5

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

Посмотрите нашу кодовую базу, чтобы понять, как мы реализовали обновляемый контракт — https://goo.gl/p5zGEv.

Примечание: в приведенном выше репозитории GitHub мы используем три уровня контрактов из-за нашего варианта использования. Однако можно сделать контракт обновляемым только с двумя слоями.

Надеюсь это поможет.

Это полезное репо. не могли бы вы поделиться последней версией Solidity?

zos представила нам платформу для простой реализации обновляемого смарт-контракта.

PTAL: https://docs.zeppelinos.org/docs/start.html

Позволяет вам иметь контракт со стабильным адресом, но полностью контролируемым и обновляемым поведением.

https://github.com/u2/эфирный маршрутизатор

https://github.com/ConsenSys/smart-contract-best-practices#upgrading-broken-contracts

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

В Blend мы использовали ZeppelinOS, чтобы сделать наши обычные смарт-контракты Ethereum обновляемыми. Вот наше пошаговое руководство и пример кода .

Настоящая проблема в обновляемом смарт-контракте заключается в переносе сохраненных значений из контракта.

Гораздо лучший способ создать обновляемый смарт-контракт — дифференцировать хранилище и логику в разных контрактах.

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

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