以太坊智慧合約開發入門
摘要
本文采用碎片化寫作,原文會不定期更新,請儘量閱讀原文。
http://www.netkiller.cn/journal/ethereum.solidity.html
目錄
- 1. Truffle
- 1.1. 安裝 Truffle
- 1.2. 建立專案
- 1.3. 配置 Truffle
- 1.4. 編譯智慧合約
- 1.5. migrate
- 1.6. 部署智慧合約
- 2. browser-solidity
- 2.1. 將 Remix(browser-solidity) 安裝到本地
- 3. 使用 solc 編譯 *.sol 程式碼
- 4. 智慧合約語言 Solidity
- 4.5.1. 構造方法
- 4.5.2. 定義函式
- 4.5.3. 函式返回值
- 4.5.4. 引數傳遞
- 4.5.5. 函式的例子
- 4.3.1. 陣列
- 4.3.2. 列舉型別
- 4.3.3. 結構體
- 4.1. 智慧合約入門演示
- 4.2.
- 4.3. 資料型別
- 4.4. 變數
- 4.5. 函式
- 4.6. 事件
- 5. Jave Client
- 5.10.1. 載入合約
- 5.9.1. 建立錢包
- 5.9.2.
- 5.8.1. 獲取餘額
- 5.8.2. 轉賬
- 5.8.3. 交易結果查詢
- 5.6.1. 獲得賬號列表
- 5.6.2. 獲得賬號資訊
- 5.6.3. 解鎖賬號
- 5.6.4. 例子
- 5.1.1. Mac OS
- 5.1.2. 二進位制包安裝
- 5.1. 安裝命令列工具
- 5.2. 啟動以太坊
- 5.3. Maven pom.xml 檔案
- 5.4. Java 與 Solidity 資料型別對映關係
- 5.5. 連線到伺服器獲取版本號
- 5.6. 賬號管理
- 5.7. Credentials
- 5.8. 交易
- 5.9.
- 5.10. 智慧合約
- 6. web3.py - A python interface for interacting with the Ethereum blockchain and ecosystem.
- 7. 總結
- 8. FAQ
1. Truffle
Truffle 是 solidity 開發框架
1.1. 安裝 Truffle
# 安裝 Nodejs curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - sudo apt-get install -y nodejs # 安裝truffle sudo npm install -g truffle # 安裝testrpc sudo npm install -g ethereumjs-testrpc
1.2. 建立專案
cd ~/ethereum
mkdir truffle-project
cd truffle-project
truffle init
操作演示
neo@netkiller ~/ethereum/truffle-project % truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
neo@netkiller ~/ethereum/truffle-project % tree
.
|-- contracts
| `-- Migrations.sol
|-- migrations
| `-- 1_initial_migration.js
|-- test
|-- truffle-config.js
`-- truffle.js
3 directories, 4 files
1.3. 配置 Truffle
開啟檔案 truffle.js
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
};
修改為
module.exports = {
// See <http://truffleframework.com/docs/advanced/configuration>
// to customize your Truffle configuration!
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*" // Match any network id
}
}
};
1.4. 編譯智慧合約
neo@netkiller ~/ethereum/truffle-project % truffle compile
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
truffle預設只會編譯最後一次修改過的合約檔案, 這是為了減少比重複編譯。"--all"選項,可以強制編譯所有檔案。
編譯結果
neo@netkiller ~/ethereum/truffle-project % find build
build
build/contracts
build/contracts/Migrations.json
1.5. migrate
neo@netkiller ~/ethereum/truffle-project % truffle migrate
Using network 'development'.
Network up to date.
1.6. 部署智慧合約
neo@netkiller ~/ethereum/truffle-project % truffle deploy
Using network 'development'.
Network up to date.
2. browser-solidity
線上使用 browser-solidity
https://ethereum.github.io/browser-solidity/ https://remix.ethereum.org/
國內網路有時不給力,建議將 Remix 安裝到本地目錄。
2.1. 將 Remix(browser-solidity) 安裝到本地
共享合約目錄
npm install -g remixd
remixd -S "/home/ethereum/codebase/blocks/contracts"
安裝 browser-solidity
git clone https://github.com/ethereum/browser-solidity
cd browser-solidity
npm install
npm run prepublish
sudo chown -R $USER:$(id -gn $USER) /home/neo/.config
npm start
啟動後瀏覽器中輸入 http://localhost:8080 可以看到 Remix 介面
Web3 Provider
Remix 提供三種執行環境,常用的有 JavaScript VM 和 Web3 Provider (連線到 --rpc --rpcaddr="0.0.0.0" --rpccorsdomain "*" --rpcport 8545)
Web3 Provider 方式需要解鎖賬號和啟動挖礦
> personal.unlockAccount(eth.accounts[0],"");
> miner.start(2); admin.sleepBlocks(1); miner.stop();
3. 使用 solc 編譯 *.sol 程式碼
neo@netkiller ~/ethereum/solidity % solc --bin --abi --optimize -o ./output helloworld.sol
neo@netkiller ~/ethereum/solidity % find output
output
output/HelloWorld.bin
output/HelloWorld.abi
4. 智慧合約語言 Solidity
Solidity 是什麼?Solidity是以太坊智慧合約的程式語言。
4.1. 智慧合約入門演示
這裡我們先做一個 Helloword 演示,讓你初步對智慧合約有一個大概的認識。
提示
需要注意的是,你在網上會看到很多例子,對照這例子一步一步操作,始終無法成功,這根Solidity的版本有很大關係。
將下面程式碼貼上到
pragma solidity ^0.4.19;
contract HelloWorld
{
string tmp;
function HelloWorld() public
{
}
function get() public constant returns (string)
{
return tmp;
}
function set(string _tmp) public
{
tmp = _tmp;
}
}
Compile - Details - WEB3DEPLOY
var helloworldContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"_tmp","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
var helloworld = helloworldContract.new(
{
from: web3.eth.accounts[0],
data: '0x6060604052341561000f57600080fd5b6102e38061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634ed3885e146100515780636d4ce63c146100ae575b600080fd5b341561005c57600080fd5b6100ac600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061013c565b005b34156100b957600080fd5b6100c1610156565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101015780820151818401526020810190506100e6565b50505050905090810190601f16801561012e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101529291906101fe565b5050565b61015e61027e565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101f45780601f106101c9576101008083540402835291602001916101f4565b820191906000526020600020905b8154815290600101906020018083116101d757829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023f57805160ff191683800117855561026d565b8280016001018555821561026d579182015b8281111561026c578251825591602001919060010190610251565b5b50905061027a9190610292565b5090565b602060405190810160405280600081525090565b6102b491905b808211156102b0576000816000905550600101610298565b5090565b905600a165627a7a72305820ea826c30d131f20a4d3a8e3fb059ffa95f4c222a5b099029750e4c1937b46e5b0029',
gas: '4700000'
}, function (e, contract){
console.log(e, contract);
if (typeof contract.address !== 'undefined') {
console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
}
})
部署智慧合約需要消耗 gas 所以你要先解鎖賬號。
> personal.unlockAccount("0x83fda0ba7e6cfa8d7319d78fa0e6b753a2bcb5a6", "", 300)
true
> var helloworldContract = web3.eth.contract([{"constant":false,"inputs":[{"name":"_tmp","type":"string"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
undefined
> var helloworld = helloworldContract.new(
... {
...... from: web3.eth.accounts[0],
...... data: '0x6060604052341561000f57600080fd5b6102e38061001e6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634ed3885e146100515780636d4ce63c146100ae575b600080fd5b341561005c57600080fd5b6100ac600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061013c565b005b34156100b957600080fd5b6100c1610156565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101015780820151818401526020810190506100e6565b50505050905090810190601f16801561012e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80600090805190602001906101529291906101fe565b5050565b61015e61027e565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101f45780601f106101c9576101008083540402835291602001916101f4565b820191906000526020600020905b8154815290600101906020018083116101d757829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061023f57805160ff191683800117855561026d565b8280016001018555821561026d579182015b8281111561026c578251825591602001919060010190610251565b5b50905061027a9190610292565b5090565b602060405190810160405280600081525090565b6102b491905b808211156102b0576000816000905550600101610298565b5090565b905600a165627a7a72305820ea826c30d131f20a4d3a8e3fb059ffa95f4c222a5b099029750e4c1937b46e5b0029',
...... gas: '4700000'
...... }, function (e, contract){
...... console.log(e, contract);
...... if (typeof contract.address !== 'undefined') {
......... console.log('Contract mined! address: ' + contract.address + ' transactionHash: ' + contract.transactionHash);
......... }
...... })
null [object Object]
undefined
helloworld 智慧合約已經建立完畢
> helloworld
{
abi: [{
constant: false,
inputs: [{...}],
name: "set",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
constant: true,
inputs: [],
name: "get",
outputs: [{...}],
payable: false,
stateMutability: "view",
type: "function"
}, {
inputs: [],
payable: false,
stateMutability: "nonpayable",
type: "constructor"
}],
address: undefined,
transactionHash: "0x466c9ad9db8f37ed5b65bc261210da92f51364ebab1dcbd3759bfc3e16ad6502"
}
4.2.
public : 函式可見性
payable :可支付的函式修飾符,沒有該修飾符無法接受轉賬操作。
msg.value :執行合約時,轉賬的eth數量,以wei為單位。
msg.sender : 執行合約的地址
4.3. 資料型別
uint 無符號整形(256bits 無符號整數) string 字串型別
4.3.1. 陣列
//建立一個memory的陣列
uint[] memory a = new uint[](7);
uint[] x = [uint(1), 3, 4];
4.3.2. 列舉型別
pragma solidity ^0.4.0;
contract Purchase {
enum State { Created, Locked, Inactive } // Enum
}
4.3.3. 結構體
定義結構體
struct Voter {
uint weight; // weight is accumulated by delegation
bool voted; // if true, that person already voted
address delegate; // person delegated to
uint vote; // index of the voted proposal
}
// This is a type for a single proposal.
struct Proposal {
bytes32 name; // short name (up to 32 bytes)
uint voteCount; // number of accumulated votes
}
4.4. 變數
address public minter;
string name;
int num;
uint constant x = 32**22 + 8;
string constant text = "abc";
bytes32 constant myHash = keccak256("abc");
uint256 ticket = 1 ether;
變數賦值
pragma solidity ^0.4.19;
contract C {
uint[] data;
function f() public view returns (uint, bool, uint) {
return (7, true, 2);
}
function g() public {
// 宣告和分配變數。 明確指定型別是不可能的。
var (x, b, y) = f();
// 分配給一個預先存在的變數。
(x, y) = (2, 7);
// 互換值的常用技巧對於非價值儲存型別不起作用。
(x, y) = (y, x);
// 元件可以省略(也可以用於變數宣告)。
// 如果元組以空元件結束,其餘的值將被丟棄。
(data.length,) = f(); // 設定長度為 7
// 在左邊也可以做同樣的事情。
(,data[3]) = f(); // Sets data[3] to 2
// 元件只能在作業的左側排除,但有一個例外:
(x,) = (1,);
// (1,) 是指定1元組元的唯一方法,因為(1)等於1。
}
}
4.5. 函式
4.5.1. 構造方法
構造方法的定義是 contract 與 function 相同
pragma solidity ^0.4.18;
contract MyContractByNetkiller {
/* Constructor */
function MyContractByNetkiller() public{
}
}
4.5.2. 定義函式
沒有返回值
function setName(string _name) public{
name = _name;
}
4.5.3. 函式返回值
有返回值
function getName() public view returns(string){
return name;
}
4.5.4. 引數傳遞
除了 f(2,3) 這樣傳遞引數,還可以使用類似字典或Map的方式 f({value: 2, key: 3});
pragma solidity ^0.4.0;
contract C {
function f(uint key, uint value) {
// ...
}
function g() {
// named arguments
f({value: 2, key: 3});
}
}
4.5.5. 函式的例子
完整的例子
pragma solidity ^0.4.18;
contract MyContractByNetkiller {
/* Constructor */
string name;
int num;
function MyContractByNetkiller() public{
name = "default";
num = 1;
}
function setName(string _name) public{
name = _name;
}
function getName() public view returns(string){
return name;
}
function setNum(int n) public{
num = n;
}
function addNum(int m) public view returns(int res){
res = m + num;
}
}
4.6. 事件
event Sent(address from, address to, uint amount);
5. Jave Client
官方網站 https://web3j.io
Java 客戶端與 Server 之間採用json-rpc協議連線。
5.1. 安裝命令列工具
web3j 命令用於將 sol 合約檔案轉換為 java 檔案。
5.1.1. Mac OS
brew tap web3j/web3j
brew install web3j
neo@MacBook-Pro ~ % web3j
_ _____ _ _
| | |____ (_) (_)
__ _____| |__ / /_ _ ___
/ / / _ '_ | | | / _
V V / __/ |_) |.___/ / | _ | || (_) |
_/_/ ___|_.__/ ____/| |(_)|_| ___/
_/ |
|__/
Usage: web3j version|wallet|solidity ...
5.1.2. 二進位制包安裝
下載二進位制檔案 https://github.com/web3j/web3j/releases
wget https://github.com/web3j/web3j/releases/download/v3.2.0/web3j-3.2.0.zip
unzip web3j-3.2.0.zip
$ ./web3j-3.2.0/bin/web3j
_ _____ _ _
| | |____ (_) (_)
__ _____| |__ / /_ _ ___
/ / / _ '_ | | | / _
V V / __/ |_) |.___/ / | _ | || (_) |
_/_/ ___|_.__/ ____/| |(_)|_| ___/
_/ |
|__/
Usage: web3j version|wallet|solidity ...
5.2. 啟動以太坊
首先啟動服務
neo@netkiller ~ % geth --networkid 123456 --rpc --rpcaddr="0.0.0.0" --rpccorsdomain "*" --nodiscover
INFO [02-01|23:35:12] Starting peer-to-peer node instance=Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.1
INFO [02-01|23:35:12] Allocated cache and file handles database=/home/neo/.ethereum/geth/chaindata cache=128 handles=1024
INFO [02-01|23:35:12] Initialised chain configuration config="{ChainID: 15 Homestead: 0 DAO: <nil> DAOSupport: false EIP150: <nil> EIP155: 0 EIP158: 0 Byzantium: <nil> Engine: unknown}"
INFO [02-01|23:35:12] Disk storage enabled for ethash caches dir=/home/neo/.ethereum/geth/ethash count=3
INFO [02-01|23:35:12] Disk storage enabled for ethash DAGs dir=/home/neo/.ethash count=2
INFO [02-01|23:35:12] Initialising Ethereum protocol versions="[63 62]" network=123456
INFO [02-01|23:35:12] Loaded most recent local header number=719 hash=61330b…82786e td=108754979
INFO [02-01|23:35:12] Loaded most recent local full block number=719 hash=61330b…82786e td=108754979
INFO [02-01|23:35:12] Loaded most recent local fast block number=719 hash=61330b…82786e td=108754979
INFO [02-01|23:35:12] Loaded local transaction journal transactions=0 dropped=0
INFO [02-01|23:35:12] Regenerated local transaction journal transactions=0 accounts=0
WARN [02-01|23:35:12] Blockchain not empty, fast sync disabled
INFO [02-01|23:35:12] Starting P2P networking
INFO [02-01|23:35:12] RLPx listener up self="enode://9f6490ffb5236f2ddc5710ae73d47c740e0a3644bbd2d67029cf4a6c4693d2f470b642fd2cc3507f7e851df60aaeb730a1270b7a477f91ec5b6b17a8a4b40527@[::]:30303?discport=0"
INFO [02-01|23:35:12] IPC endpoint opened: /home/neo/.ethereum/geth.ipc
INFO [02-01|23:35:12] HTTP endpoint opened: http://0.0.0.0:8545
INFO [02-01|23:35:15] Mapped network port proto=tcp extport=30303 intport=30303 interface="UPNP IGDv1-IP1"
Web3j 將使用這個地址連線 HTTP endpoint opened: http://your_ip_address:8545
5.3. Maven pom.xml 檔案
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.netkiller</groupId>
<artifactId>ethereum</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>ethereum</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
</project>
5.4. Java 與 Solidity 資料型別對映關係
boolean -> bool
BigInteger -> uint/int
byte[] -> bytes
String -> string and address types
List<> -> dynamic/static array
5.5. 連線到伺服器獲取版本號
package cn.netkiller.ethereum;
import java.io.IOException;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.response.Web3ClientVersion;
import org.web3j.protocol.http.HttpService;
public class Web3JClient {
// TODO Auto-generated method stub
public static void main(String[] args) {
String url = "http://172.16.0.1:8545/";
Web3j web3j = Web3j.build(new HttpService(url)); // defaults to http://localhost:8545/
try {
Web3ClientVersion web3ClientVersion = web3j.web3ClientVersion().send();
String clientVersion = web3ClientVersion.getWeb3ClientVersion();
System.out.println(clientVersion);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
執行結果
Geth/v1.7.3-stable-4bb3c89d/linux-amd64/go1.9.1
除了 TCP 方式連線,還支援 IPC 方式。這種方式比較少用,可以使用 localhost 替代。
// OS X/Linux/Unix:
Web3j web3 = Web3j.build(new UnixIpcService("/path/to/socketfile"));
...
// Windows
Web3j web3 = Web3j.build(new WindowsIpcService("/path/to/namedpipefile"));
...
5.6. 賬號管理
5.6.1. 獲得賬號列表
public List<String> getAccountlist() {
try {
return web3j.ethAccounts().send().getAccounts();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
5.6.2. 獲得賬號資訊
public String getAccount(int index) {
String account = null;
try {
account = web3j.ethAccounts().send().getAccounts().get(index);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return account;
}
5.6.3. 解鎖賬號
Admin web3j = Admin.build(new HttpService()); // defaults to http://localhost:8545/
PersonalUnlockAccount personalUnlockAccount = web3j.personalUnlockAccount("0x000...", "a password").sendAsync().get();
if (personalUnlockAccount.accountUnlocked()) {
// send a transaction
}
5.6.4. 例子
5.7. Credentials
package cn.netkiller.ethereum.credentials;
import java.io.IOException;
import java.math.BigInteger;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.ECKeyPair;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
public class CredentialsTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
String url = "http://172.16.0.1:8545/";
Web3j web3j = Web3j.build(new HttpService(url)); // defaults to http://localhost:8545/
try {
String account = web3j.ethAccounts().send().getAccounts().get(0);
Credentials credentials = Credentials.create(account);
ECKeyPair keyPair = credentials.getEcKeyPair();
BigInteger privateKey = keyPair.getPrivateKey();
BigInteger publicKey = keyPair.getPublicKey();
System.out.println(privateKey);
System.out.println("---");
System.out.println(publicKey);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
5.8. 交易
5.8.1. 獲取餘額
public BigInteger getBalance(String account) throws IOException {
EthGetBalance ethGetBalance = web3j.ethGetBalance(account, DefaultBlockParameterName.LATEST).send();
BigInteger balance = ethGetBalance.getBalance();
return balance;
}
5.8.2. 轉賬
public void transfer(String account, float coin)
throws InterruptedException, IOException, TransactionException, Exception {
String password = "";
String walletfile = "/Users/neo/netkiller/UTC--2018-01-20T04-04-06.786586541Z--83fda0ba7e6cfa8d7319d78fa0e6b753a2bcb5a6";
Credentials credentials = WalletUtils.loadCredentials(password, walletfile);
TransactionReceipt transactionReceipt = Transfer
.sendFunds(web3j, credentials, account, BigDecimal.valueOf(coin), Unit.ETHER).send();
System.out.println(transactionReceipt.getStatus());
}
5.8.3. 交易結果查詢
EthTransaction transaction = web3.ethGetTransactionByHash("TRANSACTION_HASH")
.sendAsync().get();
System.out.println(transaction.getResult());
5.9.
5.9.1. 建立錢包
package cn.netkiller.ethereum.wallet;
import java.io.File;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.WalletUtils;
public class WalletMain {
public void createWallet() throws NoSuchAlgorithmException, NoSuchProviderException,
InvalidAlgorithmParameterException, CipherException, IOException {
File file = new File("/tmp/ethereum/keystore");
String password = "passw0rd";
String fileName = WalletUtils.generateFullNewWalletFile(password, file);
System.out.println(fileName);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
WalletMain wallet = new WalletMain();
try {
wallet.createWallet();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
執行結果
neo@MacBook-Pro ~ % mkdir -p /tmp/ethereum/keystore
neo@MacBook-Pro ~ % ll /tmp/ethereum/keystore
total 8
-rw-r--r-- 1 neo wheel 491B Feb 4 18:30 UTC--2018-02-04T10-30-58.476000000Z--75d01e920d6e018445dae504058ce4d968fd2a58.json
neo@MacBook-Pro ~ % cat /tmp/ethereum/keystore/UTC--2018-02-04T10-30-58.476000000Z--75d01e920d6e018445dae504058ce4d968fd2a58.json
{"address":"75d01e920d6e018445dae504058ce4d968fd2a58","id":"80700448-69bc-475a-aaf9-f2b836f17b13","version":3,"crypto":{"cipher":"aes-128-ctr","ciphertext":"fe86f5dbd61d15d092f9d6870e70bff7ed99a7925703ea71eef23669c8e3ec62","cipherparams":{"iv":"d058819ab660cd062080b405591ba143"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"f69c535137b08667dbac53b8001313f5b43f81fce67a5d0e94b518c97d212d14"},"mac":"c247e34760bc838c3a4c8b2da286ccc6acec244bbc13fc6cc9ce28e88a7319d5"}}
5.9.2.
package cn.netkiller.ethereum.wallet;
import java.io.File;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.WalletUtils;
public class WalletMain {
public void walletAddress() throws IOException, CipherException {
File file = new File(
"/tmp/ethereum/keystore/UTC--2018-02-04T10-43-27.339000000Z--7cab470df532710d13078c5cdc0812a27f70cf51.json");
String password = "passw0rd";
Credentials credentials = WalletUtils.loadCredentials(password, file);
System.out.println(credentials.getAddress());
}
public static void main(String[] args) {
// TODO Auto-generated method stub
WalletMain wallet = new WalletMain();
try {
wallet.walletAddress();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
執行結果
0x7cab470df532710d13078c5cdc0812a27f70cf51
5.10. 智慧合約
neo@netkiller ~/ethereum/solidity % cat netkiller.sol
pragma solidity ^0.4.18;
contract Netkiller {
string name;
int num;
function Netkiller() public{
name = "default";
num = 1;
}
function setName(string _name) public{
name = _name;
}
function getName() public view returns(string){
return name;
}
function setNum(int n) public{
num = n;
}
function addNum(int m) public view returns(int res){
res = m + num;
}
}
編譯智慧合約
$ solc /path/to/<smart-contract>.sol --bin --abi --optimize -o output/
$ web3j solidity generate /path/to/<smart-contract>.bin /path/to/<smart-contract>.abi -o /path/to/src/main/java -p com.your.organisation.name
$ solc netkiller.sol --bin --abi --optimize -o output/
$ web3j solidity generate output/Netkiller.bin output/Netkiller.abi -p cn.netkiller.ethereum.contract -o java
_ _____ _ _
| | |____ (_) (_)
__ _____| |__ / /_ _ ___
/ / / _ '_ | | | / _
V V / __/ |_) |.___/ / | _ | || (_) |
_/_/ ___|_.__/ ____/| |(_)|_| ___/
_/ |
|__/
Generating cn.netkiller.ethereum.contract.Netkiller ... File written to java
neo@netkiller ~/ethereum/solidity % ll java/cn/netkiller/ethereum/contract/Netkiller.java
-rw-rw-r-- 1 neo neo 5.9K Feb 3 23:02 java/cn/netkiller/ethereum/contract/Netkiller.java
啟動以太坊,並開始挖礦。注意引數 --mine --minerthreads 1 ,你也可以啟動後在JavaScript 控制檯鍾啟動挖礦。
neo@netkiller ~ % geth --networkid 123456 --rpc --rpcaddr="0.0.0.0" --rpccorsdomain "*" --mine --minerthreads 1
package cn.netkiller.ethereum;
import java.math.BigInteger;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Contract;
import org.web3j.tx.ManagedTransaction;
import cn.netkiller.ethereum.contract.Netkiller;
public class ContractTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String walletfile = "/Users/neo/Downloads/UTC--2018-01-20T04-04-06.786586541Z--83fda0ba7e6cfa8d7319d78fa0e6b753a2bcb5a6";
Web3j web3j = Web3j.build(new HttpService("http://172.16.0.1:8545"));
Credentials credentials = WalletUtils.loadCredentials("", walletfile);
Netkiller contract = Netkiller.deploy(web3j, credentials, ManagedTransaction.GAS_PRICE, Contract.GAS_LIMIT).send();
System.out.println(contract.isValid());
if (contract.isValid()) {
System.out.println("---");
String contractAddress = contract.getContractAddress();
System.out.println(contractAddress);
System.out.println("---");
String result = contract.getName().send();
System.out.println(result);
contract.setName("Netkiller").send();
System.out.println(contract.getName().send());
System.out.println("---");
contract.setNum(BigInteger.valueOf(8)).send();
System.out.println(contract.addNum(BigInteger.valueOf(8)).send());
System.out.println("---");
} else {
System.out.println("Deploy ERROR !!!");
}
}
}
執行結果
true
---
0xef872f1b344a4b7c765c7d765a3cc82b741777a9
---
default
Netkiller
---
16
---
在程式執行是,去看 geth 列印的日誌,有如下記錄列印
INFO [02-04|00:04:43] Submitted transaction fullhash=0x9f70ccb600294d2dd6dda08d090362131b107d42a692f27dd4a3b7548dbaf22c recipient=0xEF872F1b344a4B7C765c7D765a3cC82b741777a9
5.10.1. 載入合約
HelloWorld contract = HelloWorld.load(contractAddress,web3j,credentials, ManagedTransaction.GAS_PRICE, Contract.GAS_LIMIT);
6. web3.py - A python interface for interacting with the Ethereum blockchain and ecosystem.
文件地址 http://web3py.readthedocs.io/