日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

从 Demo 中学习 Solidity

發(fā)布時間:2024/4/19 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从 Demo 中学习 Solidity 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

從 Demo 中學(xué)習(xí) Solidity [注解譯文]

(全文參考)
Solidity官方文檔

以太坊白皮書_ZH

以太坊白皮書_EN

發(fā)現(xiàn)網(wǎng)上的資料太過瑣碎, 驚奇的發(fā)現(xiàn)官方有詳細的教程, 和例子.

雖說, 是E文, 自己慢慢啃吧,也算是半學(xué)習(xí)半翻譯吧

分布式數(shù)據(jù)庫

對說區(qū)塊鏈一定形式上是分布式數(shù)據(jù)庫的理解 引用

pragma solidity ^0.4.0;contract SimpleStorage {uint storedData;function set(uint x) {storedData = x;}function get() constant returns (uint) {return storedData;} }

  第一行告訴該合約用的是0.4.0版本的solidity編寫,并且這些代碼具有向上兼容性。保證不會在不同solidity編譯版本下編譯會出現(xiàn)不同的行為。

  從Solidity角度來看,合約就是存在于以太坊區(qū)塊鏈中的一個特定地址中的代碼和數(shù)據(jù)集合。uint storedData 聲明了一個類型為 uint(256位的無符號整型)的變量,變量名稱為 storedData。你可以把它想象為數(shù)據(jù)庫中的一個字段,該字段是可以被數(shù)據(jù)庫中的方法進行查詢,修改。在以太坊中,這個字段是屬于一個合約字段。在這個例子中,該變量可以通過提供的get,set方法進行獲取或是修改。 在Solidity中,訪問一個狀態(tài)變量是不需要通過this來引用的。

  這個合約很簡單,只是允許以太坊上的任何人來存儲一個數(shù)據(jù)到某個節(jié)點,同時把這個操作發(fā)布到以太坊中,當(dāng)然,以太坊上的其他節(jié)點同樣可以通過調(diào)用set方法來修改你已經(jīng)存儲好的值。雖然有被修改,但是對該值操作的任何歷史記錄都是保存在以太坊中的。不用擔(dān)心你的存儲記錄或是修改記錄會丟失。后面我們會將到如何對合約進行限制,只允許你一個人修改這個數(shù)據(jù)

子代幣例程

這個例程是用于實現(xiàn)一個簡單的加密貨幣, 這里種幣的無中生有是可能的. 但是,只有它的合約創(chuàng)建人, 才可以實現(xiàn)這個幣的簽發(fā). 不過還好發(fā)送幣到其他的地址, 不需要注冊賬戶, 只是一個 ETH 密鑰對就好

pragma solidity ^0.4.0;contract Coin {// The keyword "public" makes those variables// readable from outside.address public minter;mapping (address => uint) public balances;// Events allow light clients to react on// changes efficiently.event Sent(address from, address to, uint amount);// This is the constructor whose code is// run only when the contract is created.function Coin() public {minter = msg.sender;}function mint(address receiver, uint amount) public {if (msg.sender != minter) return;balances[receiver] += amount;}function send(address receiver, uint amount) public {if (balances[msg.sender] < amount) return;balances[msg.sender] -= amount;balances[receiver] += amount;Sent(msg.sender, receiver, amount);} }

這個合約包含著一些新的概念, 下面一個個的明確一下

地址變量

address public minter;

這里聲明了一個地址類型的變量, 其訪問類型是Public. address 類型是一個沒有算數(shù)操作符的160位的值. 它用來存儲合約地址,或者外部用戶的密鑰對. public 關(guān)鍵字自動的使得該變量是可以被外部訪問的. 沒有 Public 的話,其他合約是無法訪問這個變量的

function minter() returns (address) { return minter; }

例如執(zhí)行這個函數(shù). 當(dāng)然, 添加這樣的一個函數(shù)可能是沒用的, 因為他的名稱和我們之前已經(jīng)聲明的變量一樣了,不過編譯器將會支出問題, 不慌…

映射(map)

下面這一行

mapping (address => uint) public balances;

也是創(chuàng)建了一個公有變量, 但是這個是一種比 address 更復(fù)雜的數(shù)據(jù)類型. 這種類型建立了 address 和 unsigned int 的映射關(guān)系.

可以想象是一個 哈希表 被初始化為, 每一個已經(jīng)存在的鍵,都和一個字節(jié)數(shù)據(jù)全是零的值相對應(yīng). 他們是成對出現(xiàn)的, 所以不可能得到全部是鍵, 或者全部是值的 一個list. (….這段好鬼復(fù)雜,后面看不懂了)
在這種情況下, getter 函數(shù) 將會被Public這個關(guān)鍵字自動創(chuàng)建, 看著和下面類似

function balances(address _account) public view returns (uint) {return balances[_account]; }

正如你所見, 你可以使用這種方法 很容易的查詢一個賬戶的余額

事件

event Sent(address from, address to, uint amount);

正如event這個名字一樣, 這行創(chuàng)建了一個事件 , 而他會在最后一行的 Send 函數(shù)觸發(fā). 當(dāng)這些事件在鏈上被觸發(fā)的時候, 用戶接口(或者服務(wù)器應(yīng)用)可以監(jiān)聽這些事件而并不需要花費很多(GAS) .

當(dāng)事件被觸發(fā)的呼喚, 監(jiān)聽這同時會收到, from , to , amount 這幾個參數(shù). 這些參數(shù)使得我們可以更容易的追蹤到這些交易.

為了監(jiān)聽到這些事件, 需要使用以下代碼

Coin.Sent().watch({}, '', function(error, result) {if (!error) {console.log("Coin transfer: " + result.args.amount +" coins were sent from " + result.args.from +" to " + result.args.to + ".");console.log("Balances now:\n" +"Sender: " + Coin.balances.call(result.args.from) +"Receiver: " + Coin.balances.call(result.args.to));} }) // watch 右括號

others

注意自動生成的balance 函數(shù)式怎么被用戶接口調(diào)用的

這在這個結(jié)構(gòu)中特定的 coin 函數(shù),是在這個合約創(chuàng)建的時候執(zhí)行的, 而不可以在以后調(diào)用.
所以這個函數(shù)用于永久的保存,合約部分屬性. 通過msg 這個全局變量( 連同一起保存的 有 tx and block).
例如: 無論從哪里調(diào)用合約函數(shù), msg.Sender 即合約創(chuàng)建者的地址

最終的, 合約中真正完成功能的,并且可以被其他合約和用戶調(diào)用的是 mint 和 send 這兩個函數(shù).

如果 mint 被非合約創(chuàng)建者(地址,用戶) ,什么都不會發(fā)生[合約中代碼可見, 直接返回了].
另一方面, send 可以被任何人(只要之前擁有這個子幣的) 用于發(fā)送到其他的地址上去.

注意 ,如果你使用這份合約來發(fā)送代幣到其他的地址, 你通過區(qū)塊瀏覽器是什么也看不到的, 因為事實上你所發(fā)送的代幣,和余額的變化, 只是儲存在特定的代幣合約的數(shù)據(jù)字段, 通過 事件的使用 ,就可以相當(dāng)容易的創(chuàng)建一個區(qū)塊瀏覽器, 來跟蹤這個新的代幣的余額和交易了
(事實上 通過第三方網(wǎng)站ETHSCAN好像可以添加新的token)

區(qū)塊鏈基礎(chǔ)(Blockchain Basics)

區(qū)塊鏈其實作為一個概念理解起來編程實現(xiàn)并不難. 因為大多數(shù)的難題(complication)(挖礦, 哈希, 橢圓曲線加密, 點對點網(wǎng)絡(luò)等) 都只是提供了一些概念和特性. 一旦你了解了這些特性, 就不用去理解其深層次的具體實現(xiàn), 比如你使用 亞馬遜的 AWS 云, 完全不用了解他的內(nèi)部網(wǎng)絡(luò)實現(xiàn)對吧?

交易(Transaction)

區(qū)塊鏈?zhǔn)且粋€全球的 共享交易數(shù)據(jù)庫. 這就意味著, 每個人加入了這個網(wǎng)絡(luò),就可以讀取整個網(wǎng)絡(luò)的數(shù)據(jù). 這樣的話, 你如果需要改變一點內(nèi)容, 那你就需要創(chuàng)建一個被其他所有人接受的所謂的交易(transaction). 交易這個詞暗示著要不就是沒有,要不就是被全部應(yīng)用(全部節(jié)點記錄).而且, 當(dāng)你的交易被記錄在數(shù)據(jù)庫中了,就沒有人能改變它.舉個例子, 想象有一個表, 列出了所有的用戶的一直數(shù)字貨幣的余額. 如果有一個轉(zhuǎn)賬請求, 數(shù)據(jù)庫實際上,就是從這一個賬戶的余額減去(Subtract)值, 另一個賬戶增加值.如果因為一些原因, 在另一賬戶增加余額不可行, 那么原賬戶也是不會改變的而且, 一次交易都會被發(fā)送者進行數(shù)字簽名(RSA 體系下,即用自己的秘鑰去加密內(nèi)容,別人用公鑰驗證簽名).這就簡單的保證了, 合法的數(shù)據(jù)庫修改. 在數(shù)字貨幣中這個簡單的檢查,確保了用于了賬戶的秘鑰(私鑰)的人,才能從賬戶轉(zhuǎn)賬

區(qū)塊

在比特幣體系中, 一個主要的要克服的障礙(obstacle)是 雙花攻擊(Double-spend).發(fā)生在當(dāng)一個當(dāng)網(wǎng)絡(luò)中的兩筆交易想同時清空同一個賬號的余額會發(fā)生什么?也許一個沖突?(這里可能是指的同時的進行a 對 b , a對 c 的轉(zhuǎn)賬)理論上(abstract)你是不必要關(guān)心這個問題的. 你的交易將會有一個特定的順序, 這些交易將會被打包(bundle)在一個叫做區(qū)塊的東西里, 然后區(qū)塊將會被處理(execute)和分發(fā)到所有的在線節(jié)點上. 如果兩個交易互相沖突(contradict), 后來的那個將會被節(jié)點拒絕, 并且不會成為節(jié)點的一部分這些區(qū)塊按時間的先后組成了一個線性的序列, 所有這也是 `區(qū)塊鏈` 這個詞的出處. 新的區(qū)塊被加入鏈中的時間間隔(intervals)差別不大, 以以太坊為例, 大約每17秒一個. 作為"序列排序機"(`order selection mechanism`)的一部分(被稱之為挖礦的行為), 可能發(fā)生,區(qū)塊回滾(reverted). 但是僅僅會在區(qū)塊的鏈尾(tip),. 隨著更多的區(qū)塊產(chǎn)生, 回滾的可能性也越小. 所以, 你的交易數(shù)據(jù)可能會被回滾, 甚至從區(qū)塊鏈中移除(..這點沒懂, 是不是叔塊和重鏈的原因?)

以太坊虛擬機(The Ethereum Virtual Machine)

概述(overview)

以太坊虛擬機(EVM) 是以太坊的智能合約的運行時環(huán)境(runtime environment).它不僅僅是一個沙箱, 并可以實現(xiàn)代碼運行和網(wǎng)絡(luò), 文件系統(tǒng), 其他進程 的完全隔離. 智能合約之間甚至也可以互相隔離.

賬戶(Account)

在以太坊中有兩種賬戶共享同一地址空間,
  • 外部賬戶 被公鑰密鑰對所控制
  • 合約賬戶 被賬號存儲的代碼所控制

    外部賬戶 是被公鑰所確定的, 而合約賬戶是在合約創(chuàng)建時確定, (合約賬戶產(chǎn)生于(derive from) 合約創(chuàng)建者的地址,而且創(chuàng)建者在其中轉(zhuǎn)入一些余額叫做nonce)

    不論是否賬戶儲存了代碼, 這兩種類型的賬戶都是被EVM 等同的對待.

交易(Transactions)

一次交易,是一個消息,從一個賬戶發(fā)送到另一個賬戶(可能是自身, 或者特殊的黑洞賬戶(zero-account), 見下), 消息可以包含二進制數(shù)據(jù)(payload)和ETHer(以太幣)如果目標(biāo)賬戶包含代碼, 并且代碼被執(zhí)行,那么這些數(shù)據(jù)將會被視為執(zhí)行的輸入數(shù)據(jù)如果目標(biāo)賬戶是零賬戶(zero-account)(地址是 0 的賬戶), 這個交易將會創(chuàng)建一個新的合約. 正如上面提到的, 新的合約地址不是 0 , 而是從創(chuàng)建者的賬戶的交易發(fā)送產(chǎn)生. 這個合約所包含的載荷(payload)被轉(zhuǎn)換為EVM的操作碼, 并且執(zhí)行. 執(zhí)行的輸出將會永久的保存在合約的代碼里.

這就意味著為了創(chuàng)建一個合約, 你不需要發(fā)送真正的代碼到合約, 使用的僅僅是這個合約執(zhí)行的返回值
(這個感覺很重要, 因為之前部署的命令沒看懂, 這個就想到于保存了句柄對吧?)
(個人理解, 就是合約創(chuàng)建時候的結(jié)果將會執(zhí)行,并保存)

Gas(特定詞匯)

每一次交易都會包含一定量的GAS.其目的是限制轉(zhuǎn)賬或者合約執(zhí)行的工作量. 當(dāng)EVM 執(zhí)行交易的時候, GAS會被按照一定規(guī)則的消耗(deplete)gas的值是可以被交易的發(fā)起者所設(shè)置的, 發(fā)起者需要支付 ` gas_price * gas ` 的量.如果在執(zhí)行交易后有GAS剩余, 那么也將會返還到發(fā)起者的賬戶上.如果gas被耗盡,(等同于(i.e.) it is negative), 一個 `out-of-gas` 的異常將會被拋出, 并且由此次交易發(fā)生的所有改變將會被回滾.

外存, 內(nèi)存,和棧

每一個用戶,都會有自己固定的內(nèi)存空間, 稱之為存儲(storge). 儲存是一個 256bit映射256bit 的鍵值對.通過合約去列舉存儲是不可能的, 因為它所消耗的代價是很大的.一個合約只能做到,讀或者寫它自己的存儲區(qū)域內(nèi)容 第二存儲區(qū)是被稱為內(nèi)存的地方, 每一個合約每一次消息調(diào)用都擁有一個,新的被清空的實例(instance).內(nèi)存是線性的,并可以做到字節(jié)等級的尋址, 但是讀取位寬限制在 256bit ,同時,寫入寬度是 8b 或者 256b.內(nèi)存拓展是以字長為單位(256b). 當(dāng)訪問一個之前不可達(untouched)的內(nèi)存字的時候, 一定量的gas 將會被消耗.內(nèi)存增長和gas消耗是呈指數(shù)的關(guān)系的.EVM 不是基于寄存器的, 而是基于棧的 機器, 所以所有的計算是通過一個稱之為棧(stack)的東西執(zhí)行的.其最大值是1024個元素(element),每個元素256bit.如棧的結(jié)構(gòu), 只可被頂端訪問, 并且遵循如下規(guī)則: 允許拷貝最上面的16個元素中的一個到棧頂或是棧頂和它下面的16個元素中的一個進行交換。所有其他操作會從棧中取出兩個(有可能是1個,多個,取決于操作)元素,把操作結(jié)果在放回棧中。當(dāng)然也有可能把棧中元素放入到存儲或是主存中,但是不可能在沒有移除上層元素的時候,隨意訪問下層元素。

指令集(instruction Set)

EVM指令集保留最小序列,用于避免不正確不完整的不一致問題. 所有的指令基于基礎(chǔ)數(shù)據(jù)類型 ,256b word. 可以實現(xiàn)常規(guī)的算術(shù), 位 及邏輯運算. 條件和無條件跳轉(zhuǎn)也是OK的.而且(furthermore),合約可以訪問當(dāng)前塊的相關(guān)屬性, 像是 序號(number) 時間戳(timestamp)

消息調(diào)用(message calls)

合約可以調(diào)用其他的合約 或者發(fā)送以太幣到其他的賬戶通過消息調(diào)用的功能.消息調(diào)用和交易類似, 一樣的擁有 源(source), 目標(biāo)(target), 載荷(payload), 以太幣, gas, 和返回數(shù)據(jù).事實上, 每一次交易都是由頂層的消息調(diào)用,繼而創(chuàng)建更多的調(diào)用.一個合約可以定義內(nèi)部消息調(diào)用需要消耗多少gas,多少gas需要被保留。如果在內(nèi)部消息調(diào)用中出現(xiàn)out-of-gas異常,合約會被通知,會在棧里用一個錯誤值來標(biāo)記。這種情況只是這次調(diào)用的gas被消耗完。在Solidity,這種情況下調(diào)用合約會引起一個人為異常,這種異常會拋出棧的信息。上面提到,調(diào)用合約會被分配到一個新的,并且是清空的主存,并能訪問調(diào)用的負載。調(diào)用負載時被稱為calldata的一個獨立區(qū)域。調(diào)用結(jié)束后,返回一個存儲在調(diào)用主存空間里的數(shù)據(jù)。這個存儲空間是被調(diào)用者預(yù)先分配好的。調(diào)用限制的深度為1024.對于更加復(fù)雜的操作,我們更傾向于使用循環(huán)而不是遞歸。

代理調(diào)用/ 代碼調(diào)用和庫(Delegatecall / Callcode and Libraries)

   存在一種特殊的消息調(diào)用,叫做代理調(diào)用。除了目標(biāo)地址的代碼在調(diào)用方的上下文中被執(zhí)行,而且msg.sender和msg.value不會改變他們的值,其他都和消息調(diào)用一樣。這就意味著合約可以在運行時動態(tài)的加載其他地址的代碼。存儲,當(dāng)前地址,余額都和調(diào)用合約有關(guān)系。只有代碼是從被調(diào)用方中獲取。這就使得我們可以在Solidity中使用庫。比如為了實現(xiàn)復(fù)雜的數(shù)據(jù)結(jié)構(gòu),可重用的代碼可以應(yīng)用于合約存儲中。

日志(logs)

   我們可以把數(shù)據(jù)存儲在一個特殊索引的數(shù)據(jù)結(jié)構(gòu)中。這個結(jié)構(gòu)映射到區(qū)塊層面的各個地方。為了實現(xiàn)這個事件,在Solidity把這個特性稱為日志。合約在被創(chuàng)建出來后是不可以訪問日志數(shù)據(jù)的。但是他們可以從區(qū)塊鏈外面有效的訪問這些數(shù)據(jù)。因為日志的部分數(shù)據(jù)是存儲在bloom filters上。我們可以用有效并且安全加密的方式來查詢這些數(shù)據(jù)。即使不用下載整個區(qū)塊鏈數(shù)據(jù)(輕客戶端)也能找到這些日志

創(chuàng)建(create)

   合約可以通過特殊的指令來創(chuàng)建其他合約。這些創(chuàng)建調(diào)用指令和普通的消息調(diào)用唯一區(qū)別是:負載數(shù)據(jù)被執(zhí)行,結(jié)果作為代碼被存儲,調(diào)用者在棧里收到了新合約的地址。

自毀(self-destruct)

   從區(qū)塊鏈中移除代碼的唯一方法是合約在它的地址上執(zhí)行了selfdestruct操作。這個賬號下剩余的以太幣會發(fā)送給指定的目標(biāo),存儲和代碼從棧中刪除。

后記

終于算是差不多的自己翻譯了第一篇文章, 翻譯的同事夜場學(xué)到了不少東西, 具體后面的EVM的架構(gòu)理解還是需要自己深入研究一下, 翻譯質(zhì)量很差,我自己都快看不懂了...

自己算是動手翻譯不易,要是有幫助或者支持的話,扔個硬幣吧(碼字不易QVQ)

ADDR: 0xf1aF694c9A24963110A334B5cede1f1BD0047041
(ETH)

總結(jié)

以上是生活随笔為你收集整理的从 Demo 中学习 Solidity的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。