编译合约

Solidity编译合约的方式有很多选择,可以通过网上Remix在线编译,也可以下载编译器本地编译。这里为了方便,选择使用本地编译。通过Web3.py官方文档,可以pip install py-solc-x包来编译,不过由于其在windows上安装依赖过于麻烦(还要装VS2019),所以还是选择用python调用node js的solcjs工具进行编译,相关代码如下。

  1. # 编译合约
  2. def __init__(self,file):
  3. os.system("solcjs "+file+" --bin --abi --optimize")
  4. for fi in os.listdir():
  5. if fi[-3:]=='abi':
  6. os.rename(fi,'tmp.abi')
  7. elif fi[-3:]=='bin':
  8. os.rename(fi,'tmp.bin')
  9. with open('tmp.bin','r') as f:
  10. self.contractBin = "0x"+(f.readlines())[0]
  11. with open('tmp.abi','r') as f:
  12. self.contractAbi = json.loads((f.readlines())[0])
  13. os.remove('tmp.abi')
  14. os.remove('tmp.bin')
  15. print('[!] 合约编译成功...')

部署合约

合约部署代码如下,注意在部署合约之前先对部署合约的账户进行解锁,并开启挖矿…
命令: personal.unlockAccount(eth.accounts[0])

  1. # 部署合约
  2. def deploy(self):
  3. self.web3 = Web3(HTTPProvider('http://localhost:60000'))
  4. if not self.web3.isConnected():
  5. exit("[!] 请检查是否开启RPC服务...")
  6. eth = self.web3.eth
  7. # 添加默认账户,该账户需要提前解锁
  8. eth.default_account = eth.accounts[0]
  9. gasValue = eth.estimateGas({'data':self.contractBin})
  10. print("[!] 合约部署预计消耗gas: "+str(gasValue))
  11. tx_hash = eth.contract(
  12. abi=self.contractAbi,
  13. bytecode=self.contractBin).constructor().transact()
  14. # 等待矿工打包区块
  15. receipt=eth.waitForTransactionReceipt(tx_hash)
  16. address = receipt['contractAddress']
  17. print("[!] 合约部署成功,地址: "+str(address))
  18. print("[!] 交易详情:"+str((receipt['transactionHash']).hex()))
  19. self.address = address

调用合约

  1. # 模拟攻击场景
  2. def attack(self):
  3. store_var_contract = self.web3.eth.contract(address=self.address, abi=self.contractAbi)
  4. gas_estimate = store_var_contract.functions.sub_underflow().estimateGas()
  5. print('[!] 调用合约函数 sub_underflow() 消耗gas:'+str(gas_estimate))
  6. print('[!] 合约返回: '+str(store_var_contract.functions.sub_underflow().call()))
  7. #receipt = self.web3.eth.waitForTransactionReceipt(tx_hash)
  8. #print(receipt)
  9. #print("[!] 合约调用成功, 交易详情:"+str((receipt['transactionHash']).hex()))
  10. #print(receipt["status"])

小结

这里通过使用python-web3.py 对合约进行部署和调用,是因为我想通过geth批量的执行命令,所以打算用python调用合约来模拟攻击过程。最好其实应该使用Node.js进行脚本编写,无奈对js不太熟悉,所以选择python。总的代码及运行结果如下:30.png

  1. import os,json,sys
  2. from web3 import Web3,HTTPProvider
  3. class contract:
  4. # 编译合约
  5. def __init__(self,file):
  6. os.system("solcjs "+file+" --bin --abi --optimize")
  7. for fi in os.listdir():
  8. if fi[-3:]=='abi':
  9. os.rename(fi,'tmp.abi')
  10. elif fi[-3:]=='bin':
  11. os.rename(fi,'tmp.bin')
  12. with open('tmp.bin','r') as f:
  13. self.contractBin = "0x"+(f.readlines())[0]
  14. with open('tmp.abi','r') as f:
  15. self.contractAbi = json.loads((f.readlines())[0])
  16. os.remove('tmp.abi')
  17. os.remove('tmp.bin')
  18. print('[!] 合约编译成功...')
  19. # 部署合约
  20. def deploy(self):
  21. self.web3 = Web3(HTTPProvider('http://localhost:60000'))
  22. if not self.web3.isConnected():
  23. exit("[!] 请检查是否开启RPC服务...")
  24. eth = self.web3.eth
  25. eth.default_account = eth.accounts[0]
  26. gasValue = eth.estimateGas({'data':self.contractBin})
  27. print("[!] 合约部署预计消耗gas: "+str(gasValue))
  28. tx_hash = eth.contract(
  29. abi=self.contractAbi,
  30. bytecode=self.contractBin).constructor().transact()
  31. receipt=eth.waitForTransactionReceipt(tx_hash)
  32. address = receipt['contractAddress']
  33. #print(dict(receipt))
  34. print("[!] 合约部署成功,地址: "+str(address))
  35. print("[!] 交易详情:"+str((receipt['transactionHash']).hex()))
  36. self.address = address
  37. # 模拟攻击场景
  38. def attack(self):
  39. store_var_contract = self.web3.eth.contract(address=self.address, abi=self.contractAbi)
  40. gas_estimate = store_var_contract.functions.sub_underflow().estimateGas()
  41. print('[!] 调用合约函数 sub_underflow() 消耗gas:'+str(gas_estimate))
  42. print('[!] 合约返回: '+str(store_var_contract.functions.sub_underflow().call()))
  43. if __name__ == "__main__":
  44. ''' python deploy.py test.sol'''
  45. contract = contract(sys.argv[1])
  46. contract.deploy()
  47. contract.attack()