美文网首页
ARTS w07- 如何升级以太坊智能合约

ARTS w07- 如何升级以太坊智能合约

作者: yanging | 来源:发表于2018-09-25 22:06 被阅读69次

Review

How to make smart contracts upgradable! 开发一个可升级的合约,可使用以下几种方式:

  • Master-Slave contracts : 发布一个主合约,这个主合约存储所有其他的合约,并可修改其他合约的地址,从而达到修改合约的目的。这种方式最简单。缺点是,合约的数据存储转移变得复杂。
  • Eternal Storage contracts: 把合约中的逻辑和数据存储分开,一个合约只实现逻辑,另一个合约只负责存储数据。数据合约不可升级,逻辑合约可升级。这种方式大多要结合上一种方式来使用,用主合约来管理数据合约和逻辑合约。缺点是,逻辑合约访问数据时要使用外部调用,要花费额外的 gas 费用,另外数据合约不可升级。
  • Upgradable Storage Proxy Contracts:可升级的存储代理合约,将外部存储合约作为逻辑合约的代理,实现方式是代理合约和逻辑合约都继承自存储合约,这样它们在 EVM 中的数据存储位置是一样的,在代理合约的 fallback 函数中使用 delegatecall 调用逻辑合约的任何函数,并可访问代理合约的存储,这样访问数据就不需要走外部调用,可以节省 gas。缺点依然是数据合约不可升级。
image
  function () payable public {
    address _impl = implementation();
    require(_impl != address(0));

    assembly {
      let ptr := mload(0x40)
      calldatacopy(ptr, 0, calldatasize)
      let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
      let size := returndatasize
      returndatacopy(ptr, 0, size)

      switch result
      case 0 { revert(ptr, size) }
      default { return(ptr, size) }
    }
  }
  • Unstructured Upgradable Storage Proxy Contracts:这个用法比较高级,要了解 EVM 存储数据的方式,见 solidity 文档 layout of state variables in storage。在代理合约中,不是直接存储逻辑合约的地址,而是存储偏移量,代码如下:
contract UpgradeabilityProxy is Proxy {
  /**
   * @dev This event will be emitted every time the implementation gets upgraded
   * @param implementation representing the address of the upgraded implementation
   */
  event Upgraded(address indexed implementation);

  // Storage position of the address of the current implementation
  bytes32 private constant implementationPosition = keccak256("org.govblocks.proxy.implementation");

  /**
   * @dev Constructor function
   */
  constructor() public {}

  /**
   * @dev Tells the address of the current implementation
   * @return address of the current implementation
   */
  function implementation() public view returns (address impl) {
    bytes32 position = implementationPosition;
    assembly {
      impl := sload(position)
    }
  }

  /**
   * @dev Sets the address of the current implementation
   * @param _newImplementation address representing the new implementation to be set
   */
  function _setImplementation(address _newImplementation) internal {
    bytes32 position = implementationPosition;
    assembly {
      sstore(position, _newImplementation)
    }
  }

  /**
   * @dev Upgrades the implementation address
   * @param _newImplementation representing the address of the new implementation to be set
   */
  function _upgradeTo(address _newImplementation) internal {
    address currentImplementation = implementation();
    require(currentImplementation != _newImplementation);
    _setImplementation(_newImplementation);
    emit Upgraded(_newImplementation);
  }
}

使用这个方法可以避免修改数据时,把代理合约的数据被覆盖了。这种方式升级合约时,新合约要继承自原来的合约,但新合约能访问原合约的数据?这点需要实际部署个合约,测试下。这个方式也要实现上面提到的 fallback 函数,这样代理合约才能调用逻辑合约的实现。

这里并没有涉及权限的约束,具体看原文,原文还指出 onlyowner 这个权限有些大,他们实现了一个由社区参与治理的权限控制,代码在这里,感兴趣的可以看看。

Tips

Feistel 密码使用了对称加密的结构,是Horst Feistel发明的加密算法。大多数分组密码的结构本质上都是基于 Feistel 网络结构。如图:


image

加密过程如下:
令 F 为轮函数,并令 K0, K1, ...Kn 分别为 0,1,...n 的子密钥。将明文块拆分为两个等长的块: (L0, R0)。对每轮 i = 0,1,...n,计算

Li+1 = Ri
Ri+1 = Li xor F(Ri, Ki)

则密文为(Rn+1, Ln+1)。
解密操作则相反,解密密文(Rn+1, Ln+1)通过计算i = n,n-1,...0

Ri = Li+1
Li = Ri+1 xor F(Li+1, Ki)

最后明文为(L0, R0)。

Feistel网络的安全性与以下参数有关:

  1. 分组大小:大的块会提高加密的安全性,但是会降低加密、解密的速度。主流的是 64 位 和 128 位。
  2. 密钥的大小:同上。
  3. 循环次数(轮次数):每多进行一轮循环,安全性就会有所提高,一般轮数取为16。
  4. 子密钥的生成算法:生成算法越复杂,则会使得密码被破译的难度增强,信息会越安全。
  5. 轮函数的复杂度:轮函数越复杂,则安全性越高。

这里有一个 python 的实现版本。

Algorithm

leetcode 205. Isomorphic Strings,判断两个字符串s、t是否同构,两个字符串同构的定义:s中的字符能否被替换成 t,替换的规则:保持字符顺序,同个字符不能被替换成两个不同的字符,字符可以替换成其自身。例如:

Input: s = "egg", t = "add"
Output: true

Input: s = "foo", t = "bar"
Output: false

Input: s = "paper", t = "title"
Output: true

实现逻辑很简单,就是依次遍历其中一个字符串,看是否能够被另一个字符串所替代:

class Solution(object):
    def isIsomorphic(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        if len(s) != len(t):
            return False
        
        dic = {};
        dic2 = {};
        for index, character in enumerate(s):
            # print(index, character, dic)
            if character not in dic: 
                if t[index] in dic2:
                    return False
                dic[character] = t[index]
                dic2[t[index]] = character
            elif dic[character] != t[index]:
                return False
        return True

Share

这几天看了08年金融危机主题的几部电影,重新理解了金融。有几句台词印象较深,记录一下。

They almost bring down the US economy as we know it, but we can't put restrictions on how they spend the $125 billion we're giving them, because the might not take it.
--《大而不倒(Too Big Too Fail)》

Too Big Too Fail

这段话挺讽刺的,那些大银行家差点搞跨了金融系统,政府出手援助却不能限制他们如何使用政府给出的借款,因为他们可能不会接受。这些钱最终没有给到那些最需要的人手里,危机导致了800万人失业,600万人无家可归。

就在同一年,2008.10.31 中本聪在密码邮件组发表了比特币白皮书。2009.1.3 中本聪挖出比特币的第一个区块——创世块,在创世区块中,中本聪写了一句话:“The Times 03/Jan/2009 Chancellor on brink of second bailout for banks.” 在区块链的世界里,货币可以不依赖第三方机构,金融机构将不再享有特权。金融体系会被改变吗?债务危机是否还会再出现?

And it's certainly no different today than it's ever been. 1637, 1979, 1819, 1837, 1857, 1884, 1901, 1929, 1937, 1974, 1987, 1992, 1997, 2000, and whatever we want to call this. It's all the same thing, over and over.
We can't help ourselves. And you and I can't control it or stop it or even slow it. We even ever so slightly alter it. We just react. And we make a lot of money if we get it right. And we get left by the side of the road if we get it wrong.
And there have always been and there always will be the same percentage of winners and losers. Happy fucks, sad sacks. Fat cats and starving dogs in this world. There may be more of us today than there's ever been. But, the percentages, they stay exactly the same.
-- 《商海通牒(Margin Call)》

历史总是一次又一次的重演,Margin Call 中投行老总的这段话,令人深思。

参考

Feistel Ciphers
费斯妥密码

相关文章

网友评论

      本文标题:ARTS w07- 如何升级以太坊智能合约

      本文链接:https://www.haomeiwen.com/subject/kxdroftx.html