Модернизируемые интеллектуальные контракты

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

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

127 голосов | спросил Mikko Ohtamaa 29 MarpmTue, 29 Mar 2016 17:48:31 +03002016-03-29T17:48:31+03:0005 2016, 17:48:31

9 ответов


121

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

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

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

Homestead Edit:

Начиная с выпуска 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;
    }
}

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

ответил Tjaden Hess 21 Jam1000000amThu, 21 Jan 2016 09:55:00 +030016 2016, 09:55:00
30

Один из способов - использовать Систему Контрактов, как описано ниже:

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

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

Вызов внешнего контракта: надежность .readthedocs.org ...

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


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

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

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

Обновление:

  1. Разверните новую версию «обновляемого» контракта с тем же адресом Register;
    • Или, может быть, если ваш Register может развернуть другие контракты - git it to him
  2. (необязательно) отключить /убить старую версию «обновляемого» контракта;
  3. Зарегистрировать адрес новой версии «обновляемого» контракта в Register.
ответил Alex Koz. 30 MarpmWed, 30 Mar 2016 17:43:14 +03002016-03-30T17:43:14+03:0005 2016, 17:43:14
27

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

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

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

Сохранение хранилища

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

Доступ к хранилищу после selfdestruct

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

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

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

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

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

ответил axic 19 MaramSat, 19 Mar 2016 00:59:21 +03002016-03-19T00:59:21+03:0012 2016, 00:59:21
20

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

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

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

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

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


EDIT: ENS, служба имен Ethereum, только что была развернута в тестовой сети (Ropsten).

Смотрите ENS wiki для quickstart и другие сведения. Вот введение:

  

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

     

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

     

ENS развертывается в тестовой сети Ropsten на   0x112234455c3a32fd11230c42e7bccd4a84e02010.

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

ответил eth 31 MaramThu, 31 Mar 2016 09:58:22 +03002016-03-31T09:58:22+03:0009 2016, 09:58:22
10

@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;
    }
}
ответил eth 17 PM00000070000003331 2016, 19:49:33
5

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

НО , вы все равно можете иметь Upgradable Smart Contracts, если учесть следующее:

Это должно быть запланировано с самого начала. Ключевым моментом является номер 4. Но все остальные необходимы для реального и плавного обновления Smart Contract Upgrade.

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

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

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

  

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

     

...

     

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

Пример 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.   
  3. Вам нужно будет тщательно подумать о том, как бороться с контрактом   данные при замене контракта

  4.   

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

Пример 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));
    }
}
  

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

     

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

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

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

Я создал рассказ об Medium для этого с заголовком: Существенное рассмотрение дизайна для Ethereum dApps (1): Upgradeable Smart Contracts , и я предоставил образец для каждой точки выше 5.

ответил Muhammad Altabba 1 Jpm1000000pmMon, 01 Jan 2018 16:40:13 +030018 2018, 16:40:13
4

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

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

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

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

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

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

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

ответил Sanchit 22 J0000006Europe/Moscow 2017, 11:57:36
2

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

https://github.com/u2/ether-router

https://github.com/ConsenSys/smart- контрактная-передовая практика, # модернизация разбитая-контракты

ответил rubyu2 24 AM00000090000001031 2016, 09:07:10
0

Реальная проблема в обновляемом смарт-контракте - переносить сохраненные значения из контракта.

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

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

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

ответил Sowmay Jain 18 J0000006Europe/Moscow 2018, 14:05:42

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132