真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Solidity語法的合約/抽象合約/接口/庫的定義是什么

這篇文章主要介紹“Solidity語法的合約/抽象合約/接口/庫的定義是什么”,在日常操作中,相信很多人在Solidity語法的合約/抽象合約/接口/庫的定義是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Solidity語法的合約/抽象合約/接口/庫的定義是什么”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

創(chuàng)新互聯(lián)于2013年開始,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目網(wǎng)站制作、成都做網(wǎng)站網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元故城做網(wǎng)站,已為上家服務(wù),為故城各地企業(yè)和個人服務(wù),聯(lián)系電話:13518219792

1

合約定義(Contract)

Solidity 合約類似于面向?qū)ο笳Z言中的類。合約中有用于數(shù)據(jù)持久化的狀態(tài)變量,和可以修改狀態(tài)變量的函數(shù)。 調(diào)用另一個合約實例的函數(shù)時,會執(zhí)行一個 EVM 函數(shù)調(diào)用,這個操作會切換執(zhí)行時的上下文,這樣,前一個合約的狀態(tài)變量就不能訪問了。

1.1 創(chuàng)建合約

可以通過以太坊交易“從外部”或從 Solidity 合約內(nèi)部創(chuàng)建合約。

一些集成開發(fā)環(huán)境,例如 Remix, 通過使用一些用戶界面元素使創(chuàng)建過程更加流暢。 在以太坊上編程創(chuàng)建合約最好使用 JavaScript API web3.js。 現(xiàn)在,我們已經(jīng)有了一個叫做 web3.eth.Contract 的方法能夠更容易的創(chuàng)建合約。

創(chuàng)建合約時,會執(zhí)行一次構(gòu)造函數(shù)(與合約同名的函數(shù))。構(gòu)造函數(shù)是可選的。只允許有一個構(gòu)造函數(shù),這意味著不支持重載。

在內(nèi)部,構(gòu)造函數(shù)參數(shù)在合約代碼之后通過 ABI 編碼 傳遞,但是如果你使用 web3.js 則不必關(guān)心這個問題。

如果一個合約想要創(chuàng)建另一個合約,那么創(chuàng)建者必須知曉被創(chuàng)建合約的源代碼(和二進制代碼)。 這意味著不可能循環(huán)創(chuàng)建依賴項。

pragma solidity ^0.4.16;

contract OwnedToken {

    // TokenCreator 是如下定義的合約類型.

    // 不創(chuàng)建新合約的話,也可以引用它。

    TokenCreator creator;

    address owner;

    bytes32 name;

    // 這是注冊 creator 和設(shè)置名稱的構(gòu)造函數(shù)。

    function OwnedToken(bytes32 _name) public {

        // 狀態(tài)變量通過其名稱訪問,而不是通過例如 this.owner 的方式訪問。

        // 這也適用于函數(shù),特別是在構(gòu)造函數(shù)中,你只能像這樣(“內(nèi)部地”)調(diào)用它們,

        // 因為合約本身還不存在。

        owner = msg.sender; 

       // 從 `address` 到 `TokenCreator` ,是做顯式的類型轉(zhuǎn)換

        // 并且假定調(diào)用合約的類型是 TokenCreator,沒有真正的方法來檢查這一點。

        creator = TokenCreator(msg.sender);

        name = _name;

    }

    function changeName(bytes32 newName) public {

        // 只有 creator (即創(chuàng)建當前合約的合約)能夠更改名稱 —— 因為合約是隱式轉(zhuǎn)換為地址的,

        // 所以這里的比較是可行的。

        if (msg.sender == address(creator))

            name = newName;

    }

    function transfer(address newOwner) public {

        // 只有當前所有者才能發(fā)送 token。

        if (msg.sender != owner) return;

        // 我們也想詢問 creator 是否可以發(fā)送。

        // 請注意,這里調(diào)用了一個下面定義的合約中的函數(shù)。

        // 如果調(diào)用失?。ū热?,由于 gas 不足),會立即停止執(zhí)行。

        if (creator.isTokenTransferOK(owner, newOwner))

            owner = newOwner;

    }

}

contract TokenCreator {

    function createToken(bytes32 name)

       public

       returns (OwnedToken tokenAddress)

    {

        // 創(chuàng)建一個新的 Token 合約并且返回它的地址。

        // 從 JavaScript 方面來說,返回類型是簡單的 `address` 類型,因為

        // 這是在 ABI 中可用的最接近的類型。

        return new OwnedToken(name);

    }

    function changeName(OwnedToken tokenAddress, bytes32 name)  public {

        // 同樣,`tokenAddress` 的外部類型也是 `address` 。

        tokenAddress.changeName(name);

    }

    function isTokenTransferOK(address currentOwner, address newOwner)

        public

        view

        returns (bool ok)

    {

        // 檢查一些任意的情況。

        address tokenAddress = msg.sender;

        return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);

    }

}

2

抽象合約(Abstract Contract)

合約函數(shù)可以缺少實現(xiàn),如下例所示(請注意函數(shù)聲明頭由 ; 結(jié)尾):

pragma solidity ^0.4.0;

contract Feline {

    function utterance() public returns (bytes32);
}

這些合約無法成功編譯(即使它們除了未實現(xiàn)的函數(shù)還包含其他已經(jīng)實現(xiàn)了的函數(shù)),但他們可以用作基類合約:

pragma solidity ^0.4.0;

contract Feline {

    function utterance() public returns (bytes32);

}

contract Cat is Feline {

    function utterance() public returns (bytes32) { return "miaow"; }

}

如果合約繼承自抽象合約,并且沒有通過重寫來實現(xiàn)所有未實現(xiàn)的函數(shù),那么它本身就是抽象的。

3

接口(Interface)

接口類似于抽象合約,但是它們不能實現(xiàn)任何函數(shù)。還有進一步的限制:

  • 無法繼承其他合約或接口。

  • 無法定義構(gòu)造函數(shù)。

  • 無法定義變量。

  • 無法定義結(jié)構(gòu)體

  • 無法定義枚舉。 將來可能會解除這里的某些限制。

接口基本上僅限于合約 ABI 可以表示的內(nèi)容,并且 ABI 和接口之間的轉(zhuǎn)換應該不會丟失任何信息。

接口由它們自己的關(guān)鍵字表示:

pragma solidity ^0.4.11;

interface Token {

    function transfer(address recipient, uint amount) public;
}

4

庫(Libary)

庫與合約類似,它們只需要在特定的地址部署一次,并且它們的代碼可以通過 EVM 的 DELEGATECALL (Homestead 之前使用 CALLCODE 關(guān)鍵字)特性進行重用。 這意味著如果庫函數(shù)被調(diào)用,它的代碼在調(diào)用合約的上下文中執(zhí)行,即 this 指向調(diào)用合約,特別是可以訪問調(diào)用合約的存儲。 因為每個庫都是一段獨立的代碼,所以它僅能訪問調(diào)用合約明確提供的狀態(tài)變量(否則它就無法通過名字訪問這些變量)。 因為我們假定庫是無狀態(tài)的,所以如果它們不修改狀態(tài)(也就是說,如果它們是 view 或者 pure 函數(shù)), 庫函數(shù)僅可以通過直接調(diào)用來使用(即不使用 DELEGATECALL 關(guān)鍵字), 特別是,除非能規(guī)避 Solidity 的類型系統(tǒng),否則是不可能銷毀任何庫的。

庫可以看作是使用他們的合約的隱式的基類合約。雖然它們在繼承關(guān)系中不會顯式可見,但調(diào)用庫函數(shù)與調(diào)用顯式的基類合約十分類似 (如果 L 是庫的話,可以使用 L.f() 調(diào)用庫函數(shù))。此外,就像庫是基類合約一樣,對所有使用庫的合約,庫的 internal 函數(shù)都是可見的。 當然,需要使用內(nèi)部調(diào)用約定來調(diào)用內(nèi)部函數(shù),這意味著所有內(nèi)部類型,內(nèi)存類型都是通過引用而不是復制來傳遞。 為了在 EVM 中實現(xiàn)這些,內(nèi)部庫函數(shù)的代碼和從其中調(diào)用的所有函數(shù)都在編譯階段被拉取到調(diào)用合約中,然后使用一個 JUMP 調(diào)用來代替 DELEGATECALL。

下面的示例說明如何使用庫(但也請務(wù)必看看 using for-https://solidity-cn.readthedocs.io/zh/develop/contracts.html?highlight=view#using-for 有一個實現(xiàn) set 更好的例子)。

 library Set {

  // 我們定義了一個新的結(jié)構(gòu)體數(shù)據(jù)類型,用于在調(diào)用合約中保存數(shù)據(jù)。

  struct Data { mapping(uint => bool) flags; }

  // 注意第一個參數(shù)是“storage reference”類型,因此在調(diào)用中參數(shù)傳遞的只是它的存儲地址而不是內(nèi)容。

  // 這是庫函數(shù)的一個特性。如果該函數(shù)可以被視為對象的方法,則習慣稱第一個參數(shù)為 `self` 。

  function insert(Data storage self, uint value)

      public

      returns (bool)

  {

      if (self.flags[value])

          return false; // 已經(jīng)存在

      self.flags[value] = true;

      return true;

  }

  function remove(Data storage self, uint value)

      public

      returns (bool)

  {

      if (!self.flags[value])

          return false; // 不存在

      self.flags[value] = false;

      return true;

  }

  function contains(Data storage self, uint value)

      public

      view

      returns (bool)

  {

      return self.flags[value];

  }

}

contract C {

    Set.Data knownValues;

    function register(uint value) public {

        // 不需要庫的特定實例就可以調(diào)用庫函數(shù),

        // 因為當前合約就是“instance”。

        require(Set.insert(knownValues, value));

    }

    // 如果我們愿意,我們也可以在這個合約中直接訪問 knownValues.flags。

}

當然,你不必按照這種方式去使用庫:它們也可以在不定義結(jié)構(gòu)數(shù)據(jù)類型的情況下使用。 函數(shù)也不需要任何存儲引用參數(shù),庫可以出現(xiàn)在任何位置并且可以有多個存儲引用參數(shù)。

調(diào)用 Set.contains,Set.insert 和 Set.remove 都被編譯為外部調(diào)用( DELEGATECALL )。 如果使用庫,請注意實際執(zhí)行的是外部函數(shù)調(diào)用。 msg.sender, msg.value 和 this 在調(diào)用中將保留它們的值, (在 Homestead 之前,因為使用了 CALLCODE,改變了 msg.sender 和 msg.value)。

以下示例展示了如何在庫中使用內(nèi)存類型和內(nèi)部函數(shù)來實現(xiàn)自定義類型,而無需支付外部函數(shù)調(diào)用的開銷:

 library BigInt {

    struct bigint {

        uint[] limbs;

    }

    function fromUint(uint x) internal pure returns (bigint r) {

        r.limbs = new uint[](1);

        r.limbs[0] = x;

    }

    function add(bigint _a, bigint _b) internal pure returns (bigint r) {

        r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length));

        uint carry = 0;

        for (uint i = 0; i < r.limbs.length; ++i) {

            uint a = limb(_a, i);

            uint b = limb(_b, i); 

           r.limbs[i] = a + b + carry;

            if (a + b < a || (a + b == uint(-1) && carry > 0))

                carry = 1;

            else

                carry = 0;

        }

        if (carry > 0) {

            // 太差了,我們需要增加一個 limb

            uint[] memory newLimbs = new uint[](r.limbs.length + 1);

            for (i = 0; i < r.limbs.length; ++i)

                newLimbs[i] = r.limbs[i];

            newLimbs[i] = carry;

            r.limbs = newLimbs;

        }

    }

    function limb(bigint _a, uint _limb) internal pure returns (uint) {

        return _limb < _a.limbs.length ? _a.limbs[_limb] : 0;

    }

    function max(uint a, uint b) private pure returns (uint) {

        return a > b ? a : b;

    }

}

contract C {

    using BigInt for BigInt.bigint;

    function f() public pure {

        var x = BigInt.fromUint(7);

        var y = BigInt.fromUint(uint(-1));

        var z = x.add(y);

    }

}

由于編譯器無法知道庫的部署位置,我們需要通過鏈接器將這些地址填入最終的字節(jié)碼中 (請參閱 使用命令行編譯器-https://solidity-cn.readthedocs.io/zh/develop/using-the-compiler.html#commandline-compiler 以了解如何使用命令行編譯器來鏈接字節(jié)碼)。 如果這些地址沒有作為參數(shù)傳遞給編譯器,編譯后的十六進制代碼將包含 Set____ 形式的占位符(其中 Set 是庫的名稱)。 可以手動填寫地址來將那 40 個字符替換為庫合約地址的十六進制編碼。

與合約相比,庫的限制:

  • 沒有狀態(tài)變量

  • 不能夠繼承或被繼承

  • 不能接收以太幣

(將來有可能會解除這些限制)

4.1 庫的調(diào)用保護

如果庫的代碼是通過 CALL 來執(zhí)行,而不是 DELEGATECALL 或者 CALLCODE 那么執(zhí)行的結(jié)果會被回退, 除非是對 view 或者 pure 函數(shù)的調(diào)用。

EVM 沒有為合約提供檢測是否使用 CALL 的直接方式,但是合約可以使用 ADDRESS 操作碼找出正在運行的“位置”。 生成的代碼通過比較這個地址和構(gòu)造時的地址來確定調(diào)用模式。

更具體地說,庫的運行時代碼總是從一個 push 指令開始,它在編譯時是 20 字節(jié)的零。當部署代碼運行時,這個常數(shù) 被內(nèi)存中的當前地址替換,修改后的代碼存儲在合約中。在運行時,這導致部署時地址是第一個被 push 到堆棧上的常數(shù), 對于任何 non-view 和 non-pure 函數(shù),調(diào)度器代碼都將對比當前地址與這個常數(shù)是否一致。

4.2 Using For

指令 using A for B; 可用于附加庫函數(shù)(從庫 A)到任何類型(B)。 這些函數(shù)將接收到調(diào)用它們的對象作為它們的第一個參數(shù)(像 Python 的 self 變量)。

using A for *; 的效果是,庫 A 中的函數(shù)被附加在任意的類型上。

在這兩種情況下,所有函數(shù)都會被附加一個參數(shù),即使它們的第一個參數(shù)類型與對象的類型不匹配。 函數(shù)調(diào)用和重載解析時才會做類型檢查。

using A for B; 指令僅在當前作用域有效,目前僅限于在當前合約中,后續(xù)可能提升到全局范圍。 通過引入一個模塊,不需要再添加代碼就可以使用包括庫函數(shù)在內(nèi)的數(shù)據(jù)類型。

讓我們用這種方式將 庫 中的 set 例子重寫:

// 這是和之前一樣的代碼,只是沒有注釋。

library Set {

  struct Data { mapping(uint => bool) flags; }

  function insert(Data storage self, uint value)

      public

      returns (bool)

  {

      if (self.flags[value])

        return false; // 已經(jīng)存在

      self.flags[value] = true;

      return true;

  }

  function remove(Data storage self, uint value)

      public

      returns (bool)

  {

      if (!self.flags[value])

          return false; // 不存在

      self.flags[value] = false;

      return true;

  }

  function contains(Data storage self, uint value)

      public

      view

      returns (bool)

  {

      return self.flags[value];

  }

}

contract C {

    using Set for Set.Data; // 這里是關(guān)鍵的修改

    Set.Data knownValues;

    function register(uint value) public {

        // Here, all variables of type Set.Data have

        // corresponding member functions.

        // The following function call is identical to

        // `Set.insert(knownValues, value)`

        // 這里, Set.Data 類型的所有變量都有與之相對應的成員函數(shù)。

        // 下面的函數(shù)調(diào)用和 `Set.insert(knownValues, value)` 的效果完全相同。

        require(knownValues.insert(value));

    }
}

也可以像這樣擴展基本類型:

library Search {

    function indexOf(uint[] storage self, uint value)

        public

        view

        returns (uint)

    {

        for (uint i = 0; i < self.length; i++)

            if (self[i] == value) return i;

        return uint(-1);

    }

}

contract C {

    using Search for uint[];

    uint[] data;

    function append(uint value) public {

        data.push(value);

    }

    function replace(uint _old, uint _new) public {

        // 執(zhí)行庫函數(shù)調(diào)用

        uint index = data.indexOf(_old);

        if (index == uint(-1)) 

           data.push(_new);

        else

            data[index] = _new;

    }

}

注意,所有庫調(diào)用都是實際的 EVM 函數(shù)調(diào)用。這意味著如果傳遞內(nèi)存或值類型,都將產(chǎn)生一個副本,即使是 self 變量。 使用存儲引用變量是唯一不會發(fā)生拷貝的情況。

到此,關(guān)于“Solidity語法的合約/抽象合約/接口/庫的定義是什么”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
文章標題:Solidity語法的合約/抽象合約/接口/庫的定義是什么
文章起源:http://weahome.cn/article/gcghoi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部