以太坊智能合約入門(編寫(xiě)、編譯魂爪、創(chuàng)建先舷、部署、交互滓侍、測(cè)試蒋川、交易)

什么是以太坊智能合約?

以太坊智能合約是存放在以太坊區(qū)塊鏈具有特定地址的代碼(它的功能)和數(shù)據(jù)(它的狀態(tài))集合撩笆。智能合約賬戶之間可以相互傳遞消息以實(shí)現(xiàn)圖靈完備運(yùn)算捺球。 智能合約以以太坊特定的二進(jìn)制字節(jié)碼通過(guò)以太坊虛擬機(jī)(EVM)運(yùn)行于區(qū)塊鏈上缸浦。

以太坊智能合約通常是以名為 Solidity 的高級(jí)語(yǔ)言編寫(xiě),并被編譯為字節(jié)碼上傳到區(qū)塊鏈上氮兵。

Solidity

Solidity是一種類似JavaScript的語(yǔ)言裂逐,允許你開(kāi)發(fā)智能合約并可以被編譯成EVM字節(jié)碼,現(xiàn)在已經(jīng)是以太坊的旗艦語(yǔ)言并且是最流行的泣栈。

編寫(xiě)合約

沒(méi)有實(shí)現(xiàn)Hello World程序的語(yǔ)言是不完整的卜高,在以太坊的環(huán)境中,Solidity沒(méi)有一個(gè)明確的方式可以”輸出”一個(gè)字符串南片。 最接近的方式就是實(shí)用日志事件將一個(gè)字符串放入?yún)^(qū)塊鏈中:

contract HelloWorld {
        event Print(string out);
        function() { Print("Hello, World!"); }
}

這條合約每次執(zhí)行后掺涛,會(huì)通過(guò)Print并帶有”Hello World”參數(shù),將一條日志放入?yún)^(qū)塊鏈中疼进。

編譯合約

可以通過(guò)多種形式的機(jī)制對(duì)solidity開(kāi)發(fā)的以太坊 智能合約的編譯薪缆。

  • 通過(guò)命令行使用 solc 編譯器。
  • 通過(guò) getheth``(仍需安裝 ``solc 編譯器) 提供的javascript控制臺(tái)使用 web3.eth.compile.solidity 伞广。
  • 通過(guò) 實(shí)時(shí)在線編譯器.
  • 通過(guò) Ethereum Wallet.

在geth中設(shè)置solidity編譯器

如果你啟動(dòng)了 geth 節(jié)點(diǎn)矮燎,你可以通過(guò)如下命令來(lái)檢查哪些編譯器可以使用。

> web3.eth.getCompilers();
["lll", "solidity", "serpent"]

這個(gè)命令返回當(dāng)前可用的編譯器的字符串?dāng)?shù)組赔癌。

Note

solc 編譯器同 cpp-ethereum 一起被安裝诞外,作為替代方案,你可以自己編譯 灾票。

如果你的 solc 執(zhí)行檔不在指定的標(biāo)準(zhǔn)路徑下峡谊,你可以通過(guò) --solc 參數(shù)指定 solc 的執(zhí)行路徑。

$ geth --solc /usr/local/bin/solc

同樣的刊苍,你可以通過(guò)命令行在運(yùn)行時(shí)執(zhí)行這個(gè)操作:

> admin.setSolc("/usr/local/bin/solc")
solc, the solidity compiler commandline interface
Version: 0.2.2-02bb315d/.-Darwin/appleclang/JIT linked to libethereum-1.2.0-8007cef0/.-Darwin/appleclang/JIT
path: /usr/local/bin/solc

編譯一個(gè)簡(jiǎn)單的合約

我們來(lái)編譯一個(gè)簡(jiǎn)單的合約代碼:

> source = "contract test {
 function multiply(uint a)
 returns(uint d)
 {
 return a * 7;
 } 
}"

這個(gè)合約提供了一個(gè)名為 multiply 的函數(shù)既们,輸入一個(gè)正整數(shù) a 返回結(jié)果 a * 7

你已經(jīng)準(zhǔn)備好了編譯solidity代碼的環(huán)境正什,使用 geth 的JS命令臺(tái) eth.compile.solidity():

> contract = eth.compile.solidity(source).test
{
  code: '605280600c6000396000f3006000357c010000000000000000000000000000000000000000000000000000000090048063c6888fa114602e57005b60376004356041565b8060005260206000f35b6000600782029050604d565b91905056',
  info: {
    language: 'Solidity',
    languageVersion: '0',
    compilerVersion: '0.9.13',
    abiDefinition: [{
      constant: false,
      inputs: [{
        name: 'a',
        type: 'uint256'
      } ],
      name: 'multiply',
      outputs: [{
        name: 'd',
        type: 'uint256'
      } ],
      type: 'function'
    } ],
    userDoc: {
      methods: {
      }
    },
    developerDoc: {
      methods: {
      }
    },
    source: 'contract test { function multiply(uint a) returns(uint d) { return a * 7; } }'
  }
}

Note

編譯器支持RPC <[https://github.com/ethereum/wiki/wiki/JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC)>__ 啥纸,因此你可以使用web3.js 并通過(guò)RPC/IPC連接到 geth

下面的例子顯示了如何通過(guò)使用JSON-RPC的 geth 來(lái)使用編譯器婴氮。

$ geth --datadir ~/eth/ --loglevel 6 --logtostderr=true --rpc --rpcport 8100 --rpccorsdomain '*' --mine console  2>> ~/eth/eth.log
$ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_compileSolidity","params":["contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"],"id":1}' http://127.0.0.1:8100

編譯器為源代碼中的每個(gè)單獨(dú)的合約生成一個(gè)合約對(duì)象斯棒,命令 eth.compile.solidity 會(huì)返回合約名和合約對(duì)象的映射。這個(gè)例子中我們的合約名為 test 主经,所以命令 eth.compile.solidity(source).test 會(huì)返回名為test的合約對(duì)象荣暮,并包含如下相關(guān)域:
code:編譯生成的以太坊虛擬機(jī)字節(jié)碼
info:編譯器輸出的額外元數(shù)據(jù)
source:源代碼
language:合約使用的編程語(yǔ)言(Solidity, Serpent, LLL)
languageVersion:合約語(yǔ)言的版本號(hào)
compilerVersion:編譯合約代碼所使用編譯器的版本號(hào)
abiDefinition:應(yīng)用程序二進(jìn)制接口定義
userDoc:提供給用戶的 [NatSpec Doc]
developerDoc:提供給開(kāi)發(fā)者的 [NatSpec Doc]

編譯器最直觀的輸出結(jié)構(gòu)(codeinfo)反應(yīng)出兩個(gè)完全不同的 部署路徑 ,編譯出的EVMcode會(huì)給發(fā)給區(qū)塊鏈上特定交易罩驻,剩下的(info)會(huì)存放在去中心化的區(qū)塊鏈云端作為完善代碼的元數(shù)據(jù)穗酥。

如果你的源代碼包含多個(gè)合約,那么輸出會(huì)包含每一個(gè)合約的入口信息,合約的展開(kāi)信息可以通過(guò)名字來(lái)獲取砾跃,你可以通過(guò)查看當(dāng)前的GlobalRegistrar合約來(lái)嘗試一下效果:

contracts = eth.compile.solidity(globalRegistrarSrc)

創(chuàng)建并部署一個(gè)合約

在開(kāi)始這個(gè)章節(jié)前骏啰,請(qǐng)確保你有一個(gè)解鎖的賬戶并且里面有一些資金。

你現(xiàn)在可以通過(guò)前面章節(jié)的EVM代碼來(lái)向一個(gè)空地址 發(fā)起一筆交易 抽高。

Note

這個(gè)可以通過(guò)更容易的方式完成判耕,也就是通過(guò) 實(shí)時(shí)在線Solidity編譯器 或者 Mix IDE

var primaryAddress = eth.accounts[0]
var abi = [{ constant: false, inputs: { name: 'a', type: 'uint256' } }]
var MyContract = eth.contract(abi)
var contract = MyContract.new(arg1, arg2, ..., {from: primaryAddress, data: evmByteCodeFromPreviousSection})

所有的二進(jìn)制數(shù)據(jù)都會(huì)被序列化為十六進(jìn)制格式厨内,十六進(jìn)制的字符串總是以 0x 作為前綴祈秕。

Note

請(qǐng)注意 arg1, arg2, ... 是合約的構(gòu)造參數(shù)渺贤,可以接受任何輸入雏胃,如果合約不需要任何構(gòu)造參數(shù)那么這些參數(shù)可以被忽略。

值得指出的是執(zhí)行這些步驟你需要支付一些費(fèi)用志鞍,一旦的交易被打包進(jìn)區(qū)塊瞭亮,你賬戶的余額會(huì)根據(jù)以太坊虛擬機(jī)的瓦斯費(fèi)用規(guī)則進(jìn)行扣除,經(jīng)過(guò)一些時(shí)間固棚,你的交易會(huì)出現(xiàn)在一個(gè)狀態(tài)被確認(rèn)是一致的區(qū)塊中统翩,你的合約現(xiàn)在已經(jīng)存在于區(qū)塊鏈中。

異步執(zhí)行這些步驟的方法如下:

MyContract.new([arg1, arg2, ...,]{from: primaryAccount, data: evmCode}, function(err, contract) {
  if (!err && contract.address)
    console.log(contract.address);
});

合約的交互

通常使用抽象層 eth.contract() 來(lái)完成與合約的交互此洲,該函數(shù)返回一個(gè)JavaScript對(duì)象厂汗,該對(duì)象包含了所有可以被JavaScript調(diào)用的合約函數(shù)。

描述合約可用函數(shù)的標(biāo)準(zhǔn)方法是 ABI定義呜师,這個(gè)對(duì)象是一個(gè)數(shù)組娶桦,該數(shù)組包含了每一個(gè)可用合約函數(shù)的調(diào)用簽名和返回值。

var Multiply7 = eth.contract(contract.info.abiDefinition);
var myMultiply7 = Multiply7.at(address);

現(xiàn)在所有ABI中定義的函數(shù)都可以在合約實(shí)例中使用了汁汗,你可以通過(guò)如下兩種方法之一來(lái)進(jìn)行調(diào)用:

> myMultiply7.multiply.sendTransaction(3, {from: address})
"0x12345"
> myMultiply7.multiply.call(3)
21

當(dāng)使用 sendTransaction 時(shí)衷畦,通過(guò)發(fā)送一個(gè)交易來(lái)調(diào)用函數(shù)。這種方式會(huì)消耗以太幣知牌,同時(shí)調(diào)用會(huì)永久被紀(jì)錄在區(qū)塊鏈中祈争,這種方式的返回值就是交易的哈希值。

當(dāng)使用 call 時(shí)角寸,函數(shù)會(huì)在本地的虛擬機(jī)(EVM)上執(zhí)行菩混,調(diào)用的返回值就是函數(shù)的返回值。這種方式的調(diào)用不會(huì)被紀(jì)錄在區(qū)塊鏈中扁藕,因此也不會(huì)改變合約的內(nèi)部狀態(tài)墨吓,這種方式被稱為常量函數(shù)調(diào)用。這種調(diào)用方式不會(huì)消耗以太幣纹磺。

只關(guān)心返回值的情況下你應(yīng)該使用 call 帖烘,如果你關(guān)心合約的狀態(tài)變化那么就使用 sendTransaction

在上面的例子中橄杨,不涉及改變合約狀態(tài)秘症,因此 sendTransaction 調(diào)用只會(huì)白白燃燒燃料(gas)增加宇宙的熵照卦。

合約元數(shù)據(jù)

在上個(gè)章節(jié)我們解釋了如何在區(qū)塊鏈上創(chuàng)建合約,接下來(lái)我們處理編譯器輸出的內(nèi)容乡摹,合約元數(shù)據(jù)或者合約信息役耕。

當(dāng)與一個(gè)你還沒(méi)有創(chuàng)建的合約進(jìn)行交互時(shí),你可能想要說(shuō)明文檔或者查看其源代碼聪廉。合約作者被鼓勵(lì)通過(guò)區(qū)塊鏈或者第三方機(jī)構(gòu)的服務(wù)來(lái)注冊(cè)此類信息瞬痘,例如: EtherChain 。API admin 為注冊(cè)了這類信息的合約提供了便利的方法來(lái)查看板熊。

// get the contract info for contract address to do manual verification
var info = admin.getContractInfo(address) // lookup, fetch, decode
var source = info.source;
var abiDef = info.abiDefinition

這項(xiàng)工作生效的基本機(jī)制是:

  • 合約信息被上傳到一個(gè)公共可訪問(wèn)的位置地址 URI
  • 知道合約地址任何人都可以找到相關(guān)的 URI

這些合約信息通過(guò)兩步區(qū)塊鏈注冊(cè)被打包: * 第一步:稱為 HashReg 的合約通過(guò)內(nèi)容哈希來(lái)注冊(cè)合約代碼框全。 * 第二步:稱為 UrlHint 的合約通過(guò)內(nèi)容哈希來(lái)注冊(cè)u(píng)rl。 這些 注冊(cè)合約 被作為前沿(Frontier)版本的一部分干签,同時(shí)被帶入到家園(Homestead)版本中津辩。

使用這個(gè)結(jié)構(gòu),只需要知道合約的地址容劳,然后獲取到url喘沿,進(jìn)而獲取合約相關(guān)的所有元數(shù)據(jù)。

如果你是一個(gè)稱職的合約創(chuàng)建者竭贩,你需要遵循如下步驟:

  1. 將合約本身部署到區(qū)塊鏈上
  2. 獲取合約信息的json文件
  3. 部署合約信息的json文件到你選擇的url上
  4. 注冊(cè)代碼哈希 -> 內(nèi)容哈希 -> url

JS API提供幫助讓這些步驟變的非常簡(jiǎn)單蚜印,調(diào)用 admin.register 來(lái)得到合約摘要,將摘要序列化存儲(chǔ)到指定的json文件中留量,計(jì)算文件的內(nèi)容哈希窄赋,并最終將這些內(nèi)容哈希注冊(cè)為代碼哈希。一單你將這些文件部署到任何url肪获,你可以通過(guò)使用 admin.registerUrl 在區(qū)塊鏈上注冊(cè)你的內(nèi)容哈希url(如果使用固定內(nèi)容尋址模型作為文檔存儲(chǔ)那么rul-hint就不是必需的了)寝凌。

source = "contract test { function multiply(uint a) returns(uint d) { return a * 7; } }"
// 使用solc來(lái)編譯
contract = eth.compile.solidity(source).test
// 創(chuàng)建合約對(duì)象
var MyContract = eth.contract(contract.info.abiDefinition)
// 合約的摘要信息,序列化到指定的json文件中
contenthash = admin.saveInfo(contract.info, "~/dapps/shared/contracts/test/info.json")
// 合約發(fā)送到區(qū)塊鏈上
MyContract.new({from: primaryAccount, data: contract.code}, function(error, contract){
  if(!error && contract.address) {
    // 計(jì)算內(nèi)容哈希并且將其通過(guò) `HashReg` 注冊(cè)為代碼哈希
    // 使用地址來(lái)發(fā)送交易
    // 返回我們用來(lái)注冊(cè)u(píng)rl的內(nèi)容哈希
    admin.register(primaryAccount, contract.address, contenthash)
    // 將 ~/dapps/shared/contracts/test/info.json 部署到一個(gè)url上
    admin.registerUrl(primaryAccount, hash, url)
  }
});

測(cè)試合約和交易

通常要對(duì)合約和交易進(jìn)行測(cè)試和調(diào)試孝赫,這個(gè)章節(jié)我們來(lái)介紹幾種調(diào)試工具和實(shí)踐方法较木。為了測(cè)試合約和交易,并且避免產(chǎn)生真實(shí)的影響青柄,你最好在一條私有區(qū)塊鏈上進(jìn)行操作伐债,這可以通過(guò)配置網(wǎng)絡(luò)ID(選擇一個(gè)獨(dú)一無(wú)二的整數(shù))來(lái)實(shí)現(xiàn),并且不需要其他對(duì)等節(jié)點(diǎn)致开。推薦的做法是為測(cè)試設(shè)置其他的數(shù)據(jù)目錄和端口峰锁,以避免可能的來(lái)自其他節(jié)點(diǎn)的影響(假設(shè)使用默認(rèn)的參數(shù)運(yùn)行)。通過(guò)調(diào)試模式來(lái)運(yùn)行 geth 双戳,并設(shè)置最高級(jí)別的日志:

geth --datadir ~/dapps/testing/00/ --port 30310 --rpcport 8110 --networkid 4567890 --nodiscover --maxpeers 0 --vmdebug --verbosity 6 --pprof --pprofport 6110 console 2>> ~/dapp/testint/00/00.log

提交任何交易前虹蒋,你需要設(shè)置好你的測(cè)試鏈,詳細(xì)內(nèi)容請(qǐng)查看: <cite style="box-sizing: border-box;">test-networks</cite>

// create account. will prompt for password
personal.newAccount();
// name your primary account, will often use it
primary = eth.accounts[0];
// check your balance (denominated in ether)
balance = web3.fromWei(eth.getBalance(primary), "ether");

// assume an existing unlocked primary account
primary = eth.accounts[0];

// mine 10 blocks to generate ether

// starting miner
miner.start(4);
// sleep for 10 blocks (this can take quite some time).
admin.sleepBlocks(10);
// then stop mining (just not to burn heat in vain)
miner.stop();
balance = web3.fromWei(eth.getBalance(primary), "ether");

創(chuàng)建交易后你可以強(qiáng)制執(zhí)行它們,如下:

miner.start(1);
admin.sleepBlocks(1);
miner.stop();

可以通過(guò)如下來(lái)檢查未驗(yàn)證的交易:

// shows transaction pool
txpool.status
// number of pending txs
eth.getBlockTransactionCount("pending");
// print all pending txs
eth.getBlock("pending", true).transactions

如果提交了交易創(chuàng)建合約魄衅,你可以檢查所需代碼是否已經(jīng)插入到當(dāng)前的區(qū)塊中:

txhash = eth.sendTansaction({from:primary, data: code})
//... mining
contractaddress = eth.getTransactionReceipt(txhash);
eth.getCode(contractaddress)

本文章內(nèi)容來(lái)源于以太坊社區(qū)峭竣,螃蟹翻譯。順便分享一個(gè)適合新手的以太坊教程晃虫,這個(gè)教程把上面的內(nèi)容講的更清楚更透徹皆撩。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市哲银,隨后出現(xiàn)的幾起案子扛吞,更是在濱河造成了極大的恐慌,老刑警劉巖荆责,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滥比,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡草巡,警方通過(guò)查閱死者的電腦和手機(jī)守呜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門型酥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)山憨,“玉大人,你說(shuō)我怎么就攤上這事弥喉∮艟梗” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵由境,是天一觀的道長(zhǎng)棚亩。 經(jīng)常有香客問(wèn)我,道長(zhǎng)虏杰,這世上最難降的妖魔是什么讥蟆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮纺阔,結(jié)果婚禮上瘸彤,老公的妹妹穿的比我還像新娘。我一直安慰自己笛钝,他們只是感情好质况,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著玻靡,像睡著了一般结榄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上囤捻,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天臼朗,我揣著相機(jī)與錄音,去河邊找鬼。 笑死视哑,一個(gè)胖子當(dāng)著我的面吹牛老厌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播黎炉,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼枝秤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了慷嗜?” 一聲冷哼從身側(cè)響起淀弹,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎庆械,沒(méi)想到半個(gè)月后薇溃,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缭乘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年沐序,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堕绩。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡策幼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奴紧,到底是詐尸還是另有隱情特姐,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布黍氮,位于F島的核電站唐含,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沫浆。R本人自食惡果不足惜捷枯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望专执。 院中可真熱鬧淮捆,春花似錦、人聲如沸他炊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)痊末。三九已至蚕苇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凿叠,已是汗流浹背涩笤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工嚼吞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蹬碧。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓舱禽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親恩沽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子誊稚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 是一段很寂寞的時(shí)光。不想對(duì)別人傾訴太多罗心,因?yàn)橄嘈懦聊拍軋?jiān)強(qiáng)里伯。也不想在這一段空白里認(rèn)真思考生活的意義。 因?yàn)槊靼撞?..
    汪小玥閱讀 266評(píng)論 0 0
  • 1渤闷、要有耐心 回想起我們學(xué)中文時(shí)疾瓮,也是從小到大系統(tǒng)的學(xué)習(xí)應(yīng)用過(guò)那么多遍,才到如今的熟練飒箭,所以對(duì)于外語(yǔ)學(xué)習(xí)狼电,給自己更...
    fish__dora閱讀 228評(píng)論 0 2
  • 感恩媽媽帶我來(lái)到這個(gè)世界!感恩媽媽的養(yǎng)育之恩弦蹂!感恩媽媽的關(guān)心肩碟! 感恩先生16年來(lái)無(wú)限的包容我,無(wú)限的愛(ài)我盈匾!祝福先生...
    椿芽?jī)合?/span>閱讀 155評(píng)論 0 1