智能合约通俗点说就是写在区块链上面的代码,代码里面编写着严谨完善的规则,一旦某个用户满足了合约里面的规则条件,就会触发里面的代码,执行某个方法。
智能合约的特点之一就是部署到链上之后不能修改,这一机制使得合约的交互方都可以信任合约。但也带来了一系列的问题,并且如果已部署的合约发现漏洞,也是无法修复的。假如发现了bug,致命性的,必须修复,那如何处理? 就是使用合约达到可升级优化才能满足需求
使已经部署上链的合约做到可优化可更改,例如链上的业务逻辑代码和状态变量达到可增删改的功能.
2. 合约升级的实现机制原理
目前实现的方式根据存储区分有各种各样的模式,但是都离不开一个最底层的机制,就是使用delegatecall的特性去实现可升级的合约,达到合约可持续优化更改的效果.
共同点:都是去调用执行目标合约地址的方法
区别:delegateCall的执行环境和call和staticCall相反,正因为这样所以可利用这种特性实现可升级,在用户层面上无感知。
具体的delegateCall的介绍可以看我另外一篇文章
Solidity--call、delegatecall 和 callcode 的区别_Zeke Luo的博客-CSDN博客
代码概述
继承openzeppelin的StorageSlotUpgradeable合约,用于插槽工具类。
// SPDX-License-Identifier: GPL-3.0
import "@openzeppelin/contracts-upgradeable/utils/StorageSlotUpgradeable.sol";
pragma solidity >=0.7.0 <0.9.0;
contract InitializedProxy {
// address of logic contract
// slot bytes32(uint256(keccak256('EIP1967.PROXY.CONFTI.IMPLEMENTATION')) - 1)
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x5f62ce3c9aebd463c7a36ab1b244d2bb94f07a2c13889b3b687940ebc467b9b3;
// ======== Constructor =========
constructor(
address logic,
bytes memory initializationCalldata
) {
require(logic != address(0),"Proxy :: Wrong proxy contract address");
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = logic;
// Delegatecall into the logic contract, supplying initialization calldata
(bool _ok, bytes memory returnData) =
logic.delegatecall(initializationCalldata);
// Revert if delegatecall to implementation reverts
require(_ok, string(returnData));
}
// ======== Fallback =========
fallback() external payable {
address _impl = StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
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)
}
}
}
// ======== Receive ===
receive() external payable {} // solhint-disable-line no-empty-blocks
function upgradeVersion(address newAddress_) public{
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newAddress_;
}
}
1.constructor构造函数拥有初始化数据,并且保存指向的业务逻辑合约
2.fallback转发接收所有业务逻辑合约的方法,
3.upgradVersion 用于升级的方法
替换指定插槽的旧逻辑合约地址,更换新的逻辑合约
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
contract logicA is ERC20Upgradeable{
function initialize(string memory tokenName_ ,string memory symbol_) initializer external {
__ERC20_init(tokenName_, symbol_);
}
function mint(address account,uint256 amount) external {
if(account != address(0) && amount > 0){
_mint(account,amount);
}
}
function burn(address account,uint256 amount) external {
if(account != address(0) && amount > 0){
_burn(account,amount);
}
}
}
此合约的主要作用是,创建可升级的逻辑合约,并且管理升级等.
contract testtFactory{
address public logicProxy;
function createProxy(address logiAddress_,string memory tokenName_,string memory symbol_) public {
bytes memory _initializationCalldata = abi.encodeWithSignature(
"initialize(string,string)",
tokenName_,
symbol_
);
logicProxy = address (new InitializedProxy(logiAddress_,_initializationCalldata));
}
function updateLogicProxy(address updataTemplate_) public {
(bool _ok, bytes memory returnData) = logicProxy.call(abi.encodeWithSignature(
"upgradeVersion(address)",
updataTemplate_
));
require(_ok, string(returnData));
}
}
createProxy : 生成可升级的代理合约
updateLogicProxy : 升级合约
contract logicA2 is ERC20Upgradeable{
function mint(address account,uint256 amount) external {
require (amount <= 10 ,"must be <= 10" );
if(account != address(0) && amount > 0){
_mint(account,amount);
}
}
function burn(address account,uint256 amount) external {
if(account != address(0) && amount > 0){
_burn(account,amount);
}
}
}
此合约修改了mint的金额必须需要小于等于10,用于升级之后的逻辑检验。
以remix做案例使用:
一.部署业务逻辑合约(可升级erc20合约)
第二步.部署工厂合约

第三步调用工厂合约创建可升级的erc20Token合约
调用createProxy传入第一步创建的可升级erc20合约地址
创建成功之后,点击logicProxy查看生成之后的代理地址

然后调用at方法,并且选择相应的逻辑合约即可调用.(at使用方式和原理可自行查看)
第四步 升级当前的erc20合约
打开工厂合约调用updateLogicProxy传入新合约的地址,即可完成升级.
(用户无感升级)

1.插槽的冲突风险
2.升级之后继承关系
合约升级风险会比较大,尽量严谨,并且升级要做到只增不减不修改.
以上就是今天要讲的内容,本文仅仅简单介绍了delegateCall的升级使用,关于安全方面还是需要自行根据业务去加限制,如有其他不正确的欢迎指出,或者DM
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为