Web3j实现智能合约
1 获取凭证
Credentials是我们钱包的凭证,在我们交易和创建智能合约的时候都需要用到。
1.1 创建新凭证
file=WalletUtils.generateFullNewWalletFile(pwd,dir);
返回的file不是全路径,而是该文件的路径名,比如UTC—2017-10-30T12-10-45.516005546Z—5f38056f45091ee992298e53681b0a60c999ff95。
前面的是创建时间,后面的是账号标识。
1.2 使用旧凭证
每个账号在创建的时候都会生成一个keystore,它是Json格式的。如果要使用旧凭证,首先需要找到我们的keystore。我这里的服务器搭建在linux服务端,所以这里把keystore拷贝到我们windows本地。
然后生成credentials
2 智能合约
2.1 编写智能合约
以太坊编写智能合约有三种语言:
- Serpent
Serpent是一种类似于Python的语言,可用于开发契约并编译为EVM字节码。它旨在最大限度地简化和清理,将低级语言的许多效率优势与易于使用的编程风格相结合,同时为合同编程添加特定领域特定功能。蛇是使用LLL编译的。
- Solidity
这是一种类似于JavaScript的语言,它允许你开发合同并编译为EVM字节码。它目前是以太坊的旗舰语言,也是最受欢迎的。
我们这里编写一份Hello World
contract HelloWorld
{
address creator;
string greeting;
function HelloWorld(string _greeting) public
{
creator = msg.sender;
greeting = _greeting;
}
function greet() constant returns (string)
{
return greeting;
}
function setGreeting(string _newgreeting)
{
greeting = _newgreeting;
}
/
Standard kill() function to recover funds
/
function kill()
{
if (msg.sender == creator)
suicide(creator); // kills this contract and sends remaining funds back to creator
}
}
- LLL
Lisp Like Language(LLL)是一种类似于Assembly的低级语言,它意味着非常简单和简约,基本上就是直接在EVM中进行编码的一个小包装。
还有一种已经被弃用了,这里我们选用官方标准的Solidity语言编写。
2.2 编译智能合约
这里使用Solidity的在线编译器https://ethereum.github.io/browser-solidity/
把我们上面编写好的智能合约代码贴进去。
然后点击Details
这里的name就是智能合约的名称,ABI文件会暴露合同的所有构造参数,我们需要用ABI文件生成java类。WEB3是生成好的js文件,可以配合web3.js直接调用。
2.3 Solidity类转换成java类
如果想要避免使用智能合约的底层实现细节,web3j提供了Solidity 智能合约包装,使您可以通过生成的包装对象直接与所有智能合约的方法进行交互。
这里我们把ABI文件复制一下,然后粘贴到本地作为.json文件,名字最好和类名一样,比如我这里叫HelloWorld.json。这里有一个大坑,就是编译器生成的JSON文件格式不对,没有合约名字,如果我们执行转换会报错。
这里我们打开json文件手动编译一下
web3j支持从Solidity ABI文件在Java中自动生成智能合约函数包装器。
web3j 命令行工具附带一个用于生成智能合同函数包装器的命令行工具:
先去官方下载一个命令行工具
https://github.com/web3j/web3j/releases/tag/v3.2.0
下载.zip文件解压到本地。
找到web3.bat文件执行 $ web3j truffle generate [—javaTypes|—solidityTypes] /path/to/.json -o /path/to/src/main/java -p com.your.organisation.name
上面这个是官方的格式不太通俗易懂,我给大家翻译一下:
Web3j truffle generate json文件地址 -o 生成的文件目录 -p 生成的文件包名。这里要注意 -o 和-p也要带上。
如果web3.bat文件点不开,就直接使用cmd命令执行,在执行的时候带上web3j的全路径就行了,执行完成之后控制条会显示:
或者也可以通过Java类进行调整,添加web3j的相关依赖,然后使用maven指定运行。
这里把生成好的java类拷贝到我们的代码里面。
2.4 发布智能合约
合约生效时间
private static final String BINARY =null;
这个BINARY代表智能合约的生效时间,生成的类里面默认是NULL. 如果不修改这个时间执行智能合约默认是失效的,所以这里我们修改一下为官方指定的二进制代码。
在编译器里面也可以看到我们生成的合约二进制文件。
就是这一行,我们拷贝过来就好了。
部署智能合约
//部署智能合约
HelloWorld contract=deploy(admin, credentials,ManagedTransaction.GAS_PRICE, Contract.GAS_LIMIT,“Hello,World”).send();
部署合约需要消耗Gas.这里说一下我对以太坊Gas的理解:
交易的过程一般需要支付一定量的手续费(当然也可以选择不支付)。矿工会优先打包交易手续费高的交易,如果没有支付交易手续费,你的交易可能要等很久才会被打包。创建一笔交易的时候不需要显式的指明支付多少交易手续费,它是根据你的 UTXO额 – 交易额 – 找零 来计算的。举个栗子,A有一个10btc的UTXO(未花费的交易输出)的支配权,它给B账户转1BTC,那么在创建交易的时候,需要指明交易额1btc,和找零8.995,那么(10-8.995-1 = 0.005)就是这笔交易的手续费,会奖励给打包包含这笔交易的区块的矿工,如果没有设置找零那么多余的9btc都会被当作交易手续费奖励给矿工,虽然你的交易会很快的被打包,但是这可能不是你想要的。
- 这样可以鼓励更加高效的合约代码,减少不必要的计算,避免系统遭受攻击,毕竟攻击者要为他们消耗的资源付出一定的代价,包括带宽,CPU,和存储。 gasPrice 是由交易的发起者来设置的,但是矿工可以选择先打包那些gas价格高的交易,gas价格低的可能要等很久或者不会被打包。
- 例如一笔交易:{ from:web3.eth.accounts[0], data:tokenCompiled.token.code, gas: 1000000 }, gas参数设置这个交易最多能使用多少gas。交易里面还可以再加一个参数gasPrice,gasPrice可以自己设置, geth会默认设置一个大多数矿工可以接受的 gasPrice, 0.05e12 wei,可以调eth.gasPrice来查看当前的gasPrice.
- 矿工在启动geth的时候可以设置两个参数—ask 和 —bid , —ask是设置一个最低的gas价格,低于这个价格的交易会被忽略,默认值是500000000000,—bid 设置gas价格竞价,默认值是 500000000000。
合约地址
//获取合约地址
String contractAddress = contract.getContractAddress();
合约发布成功之后会在ETH客户端生成一个合约地址,我们在以后调用的时候都需要用到。
验证合同是否有效
使用这种方法,您可能需要确定已经加载的合同地址是您期望的智能合约。为此,您可以使用isValid智能合约方法,只有在合约地址已部署字节码与智能合约包装中的字节码匹配时才会返回true.
System.out.println(contract.isValid());
如果返回false,可能是因为智能合约代码生成的有问题,也有可能是合同生效时间没有设置。
加载智能合约
如果我们想使用已经部署过的智能合约,这个时候我们就需要加载智能合约了,
在加载智能合约的时候你需要拥有该智能合约的地址。
HelloWorld contract =HelloWorld.load(contractAddress,admin,credentials, GAS_PRICE, GAS_LIMIT);
调用智能合约
String value=contract.greet().send();
log.info(value);
调用我们编译好的get方法,会在控制台打印Hello World。代表我们这次合约部署调用成功。
销毁智能合约
contract.kill();
销毁后的智能合约无法调用,但是可以查询到。
原文链接:http://wangxiaoming.com/blog/2018/01/22/HPB-41-ETH-3j-Smart/