在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也能夠查看到交易: