在CTF的智能合约题目里,一个很大的瓶颈(对于我本身🙁)就是不知道该如何调用合约,尤为是无源码的合约逆向。学习了一些文章,大概掌握了一些方法,所以再造个轮子记忆一下。可能还不是很全,学到新的再去补充。javascript
须要用到的工具就是Remix IDE和MetaMask插件钱包。java
固然,本身写的合约放在Remix里编译部署后就能够直接调用;若是是别人的合约,而且咱们可以拿到合约地址和源码,依然可使用Remix来调用。
这里以一道题目举例:
合约代码:python
pragma solidity ^0.4.21; contract CallMeChallenge { bool public isComplete = false; function callme() public { isComplete = true; } }
在Ropsten测试网络下,题目给出了合约源码及其地址,要求调用callme()函数。web
首先编译合约源码:
api
由于本题环境是在Ropsten测试网络下,Environment应选择“Injected Web3”,而且MetaMask是须要登陆的。浏览器
而后在“At Address”栏里填入合约地址并点击该按钮部署合约:
网络
以后会出现部署好的合约及其函数:
app
而后点击callme按钮就能调用callme函数,等待交易完成后,点击isComplete就能够看到结果为true了:
ide
在调用合约的时候我尝试更改一下代码里的函数名称,发现和原代码不同的话,部署后的合约函数没法正常调用,或许只能与原代码相同的前提下才能够调用成功。函数
这是最简单的合约调用方式,但也只能知足手动的交互。
在浏览器里按F12打开js控制台,就能够直接使用web3的库,这样的话能够直接在浏览器执行js代码调用合约。
仍是经过一道题目举例:
题目源码:
pragma solidity ^0.4.21; // Relevant part of the CaptureTheEther contract. contract CaptureTheEther { mapping (address => bytes32) public nicknameOf; function setNickname(bytes32 nickname) public { nicknameOf[msg.sender] = nickname; } } // Challenge contract. You don't need to do anything with this; it just verifies // that you set a nickname for yourself. contract NicknameChallenge { CaptureTheEther cte = CaptureTheEther(msg.sender); address player; // Your address gets passed in as a constructor parameter. function NicknameChallenge(address _player) public { player = _player; } // Check that the first character is not null. function isComplete() public view returns (bool) { return cte.nicknameOf(player)[0] != 0; } }
CaptureTheEther合约的做用是给帐户添加一个昵称,而且已经部署在了Ropsten上,地址为0x71c46Ed333C35e4E6c62D32dc7C8F00D125b4fee。咱们须要调用该合约给本身帐户添加昵称。
由于以太坊分主网和测试网络之分,我的实验了一下,如何在其它页面调用web3可能只是在主网上,若是以Ropsten网络帐户登陆下的Remix页面,则调用web3时是在Ropsten网络上操做。
把合约源码放入Remix中编译,能够获得合约的ABI:
这里的Web3Deploy是能够直接使用的JS代码,其中第一行就是合约ABI,相似JSON数据:
下面就是调用合约的JavaScript代码,在Remix页面下按F12进入js控制台里执行,并支付交易费(你在执行这段代码的时候,就是用的你此时的帐户)。
var abiDefinition = [{"constant":false,"inputs":[{"name":"nickname","type":"bytes32"}],"name":"setNickname","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"nicknameOf","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}]; var CaptureTheEther = web3.eth.contract(abiDefinition); // load from deployed contract. replace the address with yours var cte = CaptureTheEther.at("0x71c46Ed333C35e4E6c62D32dc7C8F00D125b4fee"); // call `setNickname` function cte.setNickname("your nickname", console.log);
接下来用题目生成的地址部署NicknameChallenge合约,将本身帐户地址传入构造函数,点击deploy,调用isComplete函数看到结果是true,就说明昵称添加成功。
pip安装Web3.pypip install web3
。
下面是一个调用合约函数的python脚本:
# -*- coding:utf-8 -*- from web3 import Web3, HTTPProvider true = True false = False config = { "abi":[ # 合约ABI { "constant": false, "inputs": [], "name": "callme", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "isComplete", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" } ], "address":"0x35***27" # 合约地址 } INFURA_api = "https://ropsten.infura.io/***" # INFURA的ropsten API地址 web3 = Web3(HTTPProvider(INFURA_api)) contract_instance = web3.eth.contract(address=config['address'], abi=config['abi']) my_addr = "0x97***1d" # 帐户地址 priv_key = "0xb***c" # 帐户私钥 def SendTxn(txn): signed_txn = web3.eth.account.signTransaction(txn,private_key=priv_key) res = web3.eth.sendRawTransaction(signed_txn.rawTransaction).hex() txn_receipt = web3.eth.waitForTransactionReceipt(res) print(res) return txn_receipt txn = contract_instance.functions.callme().buildTransaction( { 'chainId':3, 'nonce':web3.eth.getTransactionCount(my_addr), 'gas':7600000, 'value':Web3.toWei(0,'ether'), 'gasPrice':web3.eth.gasPrice, } ) print(SendTxn(txn))
实例化合约须要合约的ABI和地址,以及调用合约的帐户及私钥。
Remix里可直接复制到合约ABI,和地址:
这里借助INFURA(使用方法可参考以前的文章)的API链接到以太坊Ropsten网络,来监听交易。
经过建立交易的方式来调用合约函数,再将交易发送至网络。
运行脚本会输出交易信息。
而且在https://ropsten.etherscan.io也能够查看到交易: