如何編寫智能合約(Smart Contract)?(II)建立加密代幣

如何編寫智能合約(Smart Contract)?(II)建立加密代幣

接着上一篇如何編寫智能合約(Smart Contract)?,本篇文章,我們將寫一個簡單的加密代幣的智能合約來給大家詮釋加密代幣的原理,當然這篇文章只是告訴你加密代幣的原理,存在很多漏洞,不能直接使用。

啓動testrpc

打開終端,啓動testrpc,相關環境在如何編寫智能合約(Smart Contract)?這篇文章裏面已經有具體說明。

liyuechun:~ yuechunli$ testrpc
EthereumJS TestRPC v4.1.3 (ganache-core: 1.1.3)
...

接下來我們就可以一步步的創建我們的加密代幣項目了。

代幣合約的基本概念

代幣合約扮演的角色相當於銀行的角色。使用者在代幣合約中,用自己的以太幣帳戶地址當作銀行帳戶,可以透過代幣合約執行轉賬(transfer,將代幣由一個帳戶轉到另一個帳戶),查詢餘額(balanceOf,查詢指定帳戶中擁有的代幣)等原本由銀行負責的工作。因爲合約部署在公開區塊鏈上,所有的交易都是公開透明,可供檢驗的。

創建代幣合約項目

liyuechun:EncryptedToken yuechunli$ pwd
/Users/liyuechun/Desktop/SmartContractDemo/EncryptedToken
liyuechun:EncryptedToken yuechunli$ truffle init
Downloading project...
Project initialized.

  Documentation: http://truffleframework.com/docs

Commands:

  Compile: truffle compile
  Migrate: truffle migrate
  Test:    truffle test

liyuechun:EncryptedToken yuechunli$ 

新建代幣合約

終端執行truffle create contract EncryptedToken命令創建EncryptedToken.sol合約。

編寫合約代碼

將下面的合約代碼拷貝,替換EncryptedToken.sol文件的代碼。

pragma solidity ^0.4.4;

contract EncryptedToken {
  uint256 INITIAL_SUPPLY = 666666;
  mapping(address => uint256) balances;
  function EncryptedToken() {
    balances[msg.sender] = INITIAL_SUPPLY;
  }
  // 轉賬到一個指定的地址
  function transfer(address _to, uint256 _amount) {
    assert(balances[msg.sender] < _amount);
    balances[msg.sender] -= _amount;
    balances[_to] += _amount;
  }
  // 查看指定地址的餘額
  function balanceOf(address _owner) constant returns (uint256) {
    return balances[_owner];
  }
}

pragma solidity ^0.4.4中的0.4.4代表solidity的版本,^代表0.4.4 ~ 0.4.9之間的solidity都可以正常編譯當前版本的合約。

contract相當於其他語言中的classEncryptedToken相當於的名字。contract EncryptedToken可以理解爲class EncryptedToken extends Contract

uint256 INITIAL_SUPPLY = 666666聲明瞭一個變量INITIAL_SUPPLY,初始化存儲了一個666666的整數作爲部署當前合約的錢包地址的代幣數。

mapping(address => uint256) balances;balances是一個key類型爲addressvalue類型爲uint256的鍵值對(mapping),相當於Java中的map、iOS中的NSDictionary

function EncryptedToken()函數是EncryptedToken合約的構造函數(contructor),當EncryptedToken合約調用時,會先執行它的構造函數。

在構造函數中,會以當前部署合約的錢包地址爲key,以INITIAL_SUPPLYvalue初始化一個鍵值對。

function transfer(address _to, uint256 _amount) {
    assert(balances[msg.sender] < _amount);
    balances[msg.sender] -= _amount;
    balances[_to] += _amount;
}

transfer函數是聲明用來從轉賬到指定錢包地址的函數,_to代表轉賬的目的地地址,_amount代表轉賬金額。

assert(balances[msg.sender] < _amount),這句代碼中,我聲明瞭一個斷言,當balances[msg.sender] < _amount,即當前錢包餘額小於要轉賬的額度時,就會拋出異常。

balances[msg.sender] -= _amount;從當前錢包額度中減去_amount

balances[_to] += _amount;,將目標地址的額度增加_amount

 function balanceOf(address _owner) constant returns (uint256) {

    return balances[_owner];
 }

balanceOf(address _owner)函數是用來查詢指定錢包地址的餘額,_owner即是指定的錢包地址,returns (uint256)代表返回值的類型爲uint256constant關鍵字的作用是,當我們調用balanceOf函數時,它會自動調用call()方法,表明只是讀書數據,而不需要往區塊鏈寫入數據,調用這個方法,不需要花費手續費。

編譯與部署

migrations/目錄下創建一個名字叫做3_deploy_contract.js的文件。文件中的內容爲:

var EncryptedToken = artifacts.require('./EncryptedToken.sol');

module.exports = function(deployer) {
  deployer.deploy(EncryptedToken);
}

接下來執行compilemigrate命令:

liyuechun:EncryptedToken yuechunli$ truffle compile
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/EncryptedToken.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

liyuechun:EncryptedToken yuechunli$ truffle migrate
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x8b31575b8ac09efae7bdf933823fcf91885b4e1c93b4d9b37421d22ab73cef25
  Migrations: 0x9b34fffa8e0d2e6d09f59ee289db13e0662b68fe
Saving successful migration to network...
  ... 0x45ba02af03a7e94f66bebbb72eebf547ffe128a49f01437aca6b45c2f489b0a1
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying ConvertLib...
  ... 0xae9c5fd334d8925b027d8de3ca9aeb13e2bd1d2d3b95b49ef3c7b472446d73ae
  ConvertLib: 0x3f7fff3afe38a0a152806a0f4f416e52a338829f
  Linking ConvertLib to MetaCoin
  Deploying MetaCoin...
  ... 0x8da8a4c881eb9bb62a1477db13044ce84707aac0df54f7ab1baf8b7f0904d251
  MetaCoin: 0x7b9cc3ebba27a1ef8dda0b1825ae47369d647739
Saving successful migration to network...
  ... 0x319cebe2651f0dfbb4adb0a187745a6d47bde22243e4cb32346790ea9abbc5c8
Saving artifacts...
Running migration: 3_deploy_contract.js
  Deploying EncryptedToken...
  ... 0x5e6203aaf1280bac1f991cb3d2858342d9287c0970da8068eeae657b54022cab
  EncryptedToken: 0x16685fb5d7e88293fc1c79a0428f3ab9d2165384
Saving successful migration to network...
  ... 0x713227a22cafea3ff1fa3bd35ff38cb367d9c8dbdc698124053f72020d37bf28
Saving artifacts...
liyuechun:EncryptedToken yuechunli$ 

如上所示,我們已經將EncryptedToken代幣合約部署到了testrpc上。

合約驗證

合約部署完成後,我們通過truffle console開啓console控制檯,在這個控制檯中對已經部署的合約進行驗證。

liyuechun:EncryptedToken yuechunli$ truffle console
truffle(development)> web3.eth.coinbase
'0x38ae16e70a31ba8679cdcac1cac6ffda7226f2d1'
truffle(development)> web3.eth.accounts[0]
'0x38ae16e70a31ba8679cdcac1cac6ffda7226f2d1'
truffle(development)> web3.eth.accounts[1]
'0xedbbf7b06f3f35fd32ab8157e6cd4ced257451e8'
truffle(development)> web3.eth.accounts[2]
'0xfdb73d8ed20f7e9981264be7639346e57db28698'
truffle(development)> web3.eth.accounts[3]
'0x77f0688b69c236f0fb9264a9daf8ca06952f278f'
truffle(development)> web3.eth.accounts[4]
'0x4f1ed5c8c87fad9c37ab509203026d041e00417b'
truffle(development)> web3.eth.accounts[5]
'0xdead10c6c02ef4121e0d2f63d979daa3b9a692be'
truffle(development)> web3.eth.accounts[6]
'0xf265663d195078f445a08966a660fbffff421d20'
truffle(development)> web3.eth.accounts[7]
'0x1d822a897b9ca697ee03766b3d13a5a0c7235b78'
truffle(development)> web3.eth.accounts[8]
'0xafcc21dc7a09c8e23532aba4c8cc64b1b7831892'
truffle(development)> web3.eth.accounts[9]
'0x4693ab1bc520ac1314e77077645b7ee15f7510ce'
truffle(development)> 

testrpc啓動時,系統會給我們分配10個錢包地址,如上圖所示我們可以通過web3.eth.coinbase或者web3.eth.accounts[0]獲取首個錢包地址,當然也可以根據索引獲取其他的錢包地址。

接下來聲明一個合約變量存儲EncryptedToken合約實例。

truffle(development)> let contract;
undefined
truffle(development)> EncryptedToken.deployed().then(instance => contract = instance)
.....
truffle(development)> 

驗證web3.eth.coinbaseweb3.eth.accounts[1]中的餘額。

truffle(development)> contract.balanceOf(web3.eth.coinbase)
{ [String: '666666'] s: 1, e: 5, c: [ 666666 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
truffle(development)> 

經驗證,第0個錢包地址中的代幣餘額爲666666,第1個錢包地址中的代幣餘額爲0

下一步,我們將從第0個賬號中向第1個賬號轉賬666個代幣。

truffle(development)> contract.transfer(web3.eth.accounts[1], 666)
Error: VM Exception while processing transaction: invalid opcode
    at Object.InvalidResponse (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:37295:16)
    at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:224765:36
    at /usr/local/lib/node_modules/truffle/build/cli.bundled.js:208348:9
    at XMLHttpRequest.request.onreadystatechange (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:209773:13)
    at XMLHttpRequestEventTarget.dispatchEvent (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:67130:18)
    at XMLHttpRequest._setReadyState (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:67420:12)
    at XMLHttpRequest._onHttpResponseEnd (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:67575:12)
    at IncomingMessage.<anonymous> (/usr/local/lib/node_modules/truffle/build/cli.bundled.js:67535:24)
    at emitNone (events.js:110:20)
    at IncomingMessage.emit (events.js:207:7)
truffle(development)> 

如上所示,轉賬過程中出現了異常,轉賬失敗,仔細檢查一下,不難發現是因爲在我們合約代碼EncryptedToken.sol中有這麼一句代碼assert(balances[msg.sender] < _amount);,也就是說只有當balances[msg.sender]小於_amount時,纔不會出現異常,所以我們應該將<符號換成>符號,即當balances[msg.sender]餘額不足時拋出異常。

代碼修改後,需要重新編譯,部署。

liyuechun:EncryptedToken yuechunli$ pwd
/Users/liyuechun/Desktop/SmartContractDemo/EncryptedToken
liyuechun:EncryptedToken yuechunli$ ls
build       contracts   migrations  test        truffle.js
liyuechun:EncryptedToken yuechunli$ rm -rf build/
liyuechun:EncryptedToken yuechunli$ ls
contracts   migrations  test        truffle.js
liyuechun:EncryptedToken yuechunli$ truffle compile
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/EncryptedToken.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts

liyuechun:EncryptedToken yuechunli$ truffle migrate --reset
Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x04c008d8e7008afa1ace7c0dc294eb2e5b02a94960e559811d12187c53b07e56
  Migrations: 0x192929621fd7cf4dd2ac72b42c5f52b40ccfa999
Saving successful migration to network...
  ... 0xd4c65a206e68caab63957343e59282f7a34d92b0f1e1ac99cf77c127d014f958
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying ConvertLib...
  ... 0x4d9e476d05e1b7aea3764c0fd44799ce984d04006435d2959a87b97f62ee2c5a
  ConvertLib: 0xdf7dc01c43772881f21f857f2d689ea591a66a20
  Linking ConvertLib to MetaCoin
  Deploying MetaCoin...
  ... 0xe4ef8fc746d9772a964d8137af4c14eab4c03a7071e1b1b1f27ec963a28558b0
  MetaCoin: 0x685a10d1a8d5196e20a8ae31e0fe80f122e4dd47
Saving successful migration to network...
  ... 0xdd3a300b3f22997540ae019f31f0de4b6b8117cf1dcbc61ed5c2b25bb8011e7c
Saving artifacts...
Running migration: 3_deploy_contract.js
  Deploying EncryptedToken...
  ... 0x08ff437889f6bca7650550491937ed078d09bbbec5a2986146824ba7b7bd0e27
  EncryptedToken: 0xe4de630b2c2818dc18a1ac16935a945634c33a03
Saving successful migration to network...
  ... 0x6eadbc688aca46b34d11696ceca3564ded694dec772412ecf3da4cb6310f7d23
Saving artifacts...
liyuechun:EncryptedToken yuechunli$ 

備註:編譯時,一定要先將build文件夾刪除,其次在部署合約時,一定要添加--reset,否則修改後的合約沒法部署成功。

打開控制檯,按照如下操作進行驗證。

liyuechun:EncryptedToken yuechunli$ truffle console
truffle(development)> let contract
undefined
truffle(development)> EncryptedToken.deployed().then(instance => contract = instance)
truffle(development)> contract.balanceOf(web3.eth.coinbase)
{ [String: '666666'] s: 1, e: 5, c: [ 666666 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
{ [String: '0'] s: 1, e: 0, c: [ 0 ] }
truffle(development)> contract.transfer(web3.eth.accounts[1], 666)
{ tx: '0x93a38d9c86520cdd5c033511bb67f4aa5230811acf8eb6a703d907d31a4d044d',
  receipt: 
   { transactionHash: '0x93a38d9c86520cdd5c033511bb67f4aa5230811acf8eb6a703d907d31a4d044d',
     transactionIndex: 0,
     blockHash: '0xe02e7c9859d43e398bf0ece48e3a15510bb76beefa9bd48ce46a39022ccf5a43',
     blockNumber: 31,
     gasUsed: 48952,
     cumulativeGasUsed: 48952,
     contractAddress: null,
     logs: [] },
  logs: [] }
truffle(development)> contract.balanceOf(web3.eth.coinbase)
{ [String: '666000'] s: 1, e: 5, c: [ 666000 ] }
truffle(development)> contract.balanceOf(web3.eth.accounts[1])
{ [String: '666'] s: 1, e: 2, c: [ 666 ] }
truffle(development)> 

如上所示,代幣轉賬成功。

總結

在這篇文章中,只是簡單介紹了代幣系統的邏輯,並沒有對安全進行相關操作,比如:餘額不夠的處理、地址合不合法的處理等等。當然,通過這篇文章的學習,你再回頭看我們初始項目中的MetaCoin代碼時,你就應該能看懂了。

打賞地址

**比特幣:**1FcbBw62FHBJKTiLGNoguSwkBdVnJQ9NUn
**以太坊:**0xF055775eBD516e7419ae486C1d50C682d4170645

技術交流

  • 區塊鏈技術交流QQ羣:348924182

  • 「區塊鏈部落」官方公衆號

參考資料

發佈了94 篇原創文章 · 獲贊 31 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章