<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          微眾銀行Solidity智能合約庫:區(qū)塊鏈工程師的隨身工具箱

          共 10444字,需瀏覽 21分鐘

           ·

          2021-04-20 10:38

          區(qū)塊鏈技術(shù)在經(jīng)歷了十余年的發(fā)展后,漸呈“燎原之勢”,不斷在各行業(yè)落地生根。但同時(shí),從技術(shù)的角度看,區(qū)塊鏈應(yīng)用開發(fā)仍然有著較高的門檻,存在不少痛點(diǎn)。為了提升應(yīng)用開發(fā)各環(huán)節(jié)的用戶體驗(yàn),微眾銀行將自主研發(fā)的區(qū)塊鏈應(yīng)用開發(fā)組件WeBankBlockchain-SmartDev全面開源,多維度提速區(qū)塊鏈應(yīng)用開發(fā)效率。開源公告詳見《區(qū)塊鏈應(yīng)用開發(fā)組件:助力低代碼開發(fā)》。


          WeBankBlockchain-SmartDev所包含的智能合約庫組件,涵蓋了從基礎(chǔ)類型到上層業(yè)務(wù)的常見Solidity庫代碼,用戶可根據(jù)實(shí)際需要進(jìn)行參考、復(fù)用。該智能合約庫已經(jīng)集成到國家信息中心頂層設(shè)計(jì)的區(qū)塊鏈服務(wù)網(wǎng)絡(luò)BSN、微眾銀行自主研發(fā)的區(qū)塊鏈中間件平臺(tái)WeBASE中,并在供應(yīng)鏈金融、存證、農(nóng)牧溯源等多個(gè)業(yè)務(wù)場景中廣泛應(yīng)用。我們期待區(qū)塊鏈技術(shù)愛好者和開源社區(qū)的伙伴們,一同參與共建,協(xié)力推動(dòng)Solidity智能合約庫向更成熟的技術(shù)、更完善的生態(tài)上發(fā)展。


            智能合約庫簡介


          作為一門實(shí)現(xiàn)了圖靈完備的智能合約編程語言,Solidity編程語言的開發(fā)、設(shè)計(jì)、迭代、演化的邏輯完全基于區(qū)塊鏈,并在區(qū)塊鏈領(lǐng)域具有廣泛的影響力和詳盡的文檔,被眾多區(qū)塊鏈底層平臺(tái)所支持,其中就包括FISCO BCOS。


          但是,Solidity編程語言也存在若干挑戰(zhàn)。首先,受區(qū)塊鏈昂貴的資源限制,Solidity舍去了諸多在其他語言中常見的特性,例如高級(jí)的語法等。其次,流行的Solidity智能合約庫多為公有鏈所開發(fā),與FISCO BCOS存在兼容性的問題。最后,智能合約編程的安全性要求高,且較難對(duì)合約進(jìn)行升級(jí),一旦存在安全漏洞,后果不堪設(shè)想。


          為了解決上述問題,WeBankBlockchain-SmartDev-Contract智能合約庫應(yīng)運(yùn)而生,包含了基礎(chǔ)類型、數(shù)據(jù)結(jié)構(gòu)、通用功能、上層業(yè)務(wù)等智能合約庫。用戶可根據(jù)實(shí)際需求進(jìn)行參考、復(fù)用。智能合約庫的設(shè)計(jì)初衷是提供場景化、通用化、可插拔的智能合約代碼,從而最大程度地節(jié)約開發(fā)智能合約的時(shí)間,改變智能合約工具庫匱乏的局面。


          SmartDev-Contract智能合約庫是一個(gè)“麻雀雖小,五臟俱全”的智能合約的工具類庫,通過Solidity的library封裝,旨在幫助Solidity開發(fā)者提升開發(fā)體驗(yàn),避免重復(fù)造輪子,讓編寫Solidity語言也可以如編寫Python語言那樣“絲滑順暢”。


          SmartDev-Contract智能合約庫的每個(gè)合約文件都來自于微眾銀行區(qū)塊鏈工程師的細(xì)致打磨,來自于實(shí)際使用場景的“聚沙成塔”,覆蓋了業(yè)務(wù)場景開發(fā)中的各種“犄角旮旯”,是開發(fā)智能合約的“10倍工程師”的不二法門。


          從功能上來看,SmartDev-Contract智能合約庫涵蓋了從基礎(chǔ)類型到上層業(yè)務(wù)的常見代碼,用戶可根據(jù)實(shí)際需要進(jìn)行參考、復(fù)用。具體如下:



            痛點(diǎn)及解決方式


          以在Solidity語言中將address類型轉(zhuǎn)為string為例。


          過去:
          打開搜索引擎或github->搜索關(guān)鍵字“Solidity address convert to string”->找到相關(guān)的搜索結(jié)果->拷貝相關(guān)的代碼->粘貼到自己的智能合約代碼中。

          如果無法搜索到相關(guān)的代碼,開發(fā)者必須重新造輪子,耗時(shí)耗力的同時(shí),還可能引入新的風(fēng)險(xiǎn)。


          現(xiàn)在:

          直接下載智能合約代碼庫->解壓->找到相關(guān)的庫合約->引入代碼->調(diào)用相關(guān)函數(shù)。智能合約代碼庫地址:

          https://github.com/WeBankBlockchain/SmartDev-Contract/archive/refs/tags/V1.0.0.zip

          pragma solidity ^0.4.25;import "./LibAddress.sol" contract Demo {    ...    address addr = 0xE0f5206BBD039e7b0592d8918820024e2a7437b9;    bytes memory bs = LibAddress.addressToBytes(addr);}


          開發(fā)者引入或拷貝網(wǎng)絡(luò)上未知來源的代碼可能出現(xiàn)重大的bug。同樣的,自己重新編寫的代碼可能因?yàn)槿狈y試或?qū)嵺`檢驗(yàn),更易出現(xiàn)風(fēng)險(xiǎn)。智能合約庫提供了方便、成熟、安全、低成本的解決方案。


          痛點(diǎn)一:計(jì)算可能溢出


          在智能合約的開發(fā)中,數(shù)值計(jì)算問題不可避免。但是,Solidity內(nèi)置的運(yùn)算機(jī)制不夠安全,因計(jì)算問題導(dǎo)致的智能合約安全事故屢見不鮮。


          SmartDev-Contract智能合約庫提供了安全計(jì)算的代碼類庫。以u(píng)int256數(shù)據(jù)類型為例,LibSafeMathForUint256Utils提供了Uint256類型的相關(guān)計(jì)算操作,且保證數(shù)據(jù)的正確性和安全性,包括加法、減法、乘法、除法、取模、乘方、最大值、最小值和平均數(shù)等操作。其他的數(shù)值類型可以自行參考實(shí)現(xiàn)。


          1、加減乘除運(yùn)算

          function f() public view {    uint256 a = 25;    uint256 b = 20;    // a + b    uint256 c = LibSafeMathForUint256Utils.add(a,b);    // a - b    uint256 d = LibSafeMathForUint256Utils.sub(a,b);    // a * b    uint256 e = LibSafeMathForUint256Utils.mul(a,b);    // a/b    uint256 f = LibSafeMathForUint256Utils.div(a,b);}


          2、取模運(yùn)算、乘方運(yùn)算

          function f() public view {    uint256 a = 25;    uint256 b = 20;    // a % b    uint256 c = LibSafeMathForUint256Utils.mod(a,b);    // a ^ b    uint256 d = LibSafeMathForUint256Utils.power(a,b);}

          3、最大值、最小值、平均數(shù)運(yùn)算

          function f() public view {    uint256 a = 25;    uint256 b = 20;    // max(a, b)    uint256 c= LibSafeMathForUint256Utils.max(a,b);    // min(a, b)    uint256 d = LibSafeMathForUint256Utils.min(a,b);    // average(a, b)    uint256 e = LibSafeMathForUint256Utils.average(a,b);}


          痛點(diǎn)二:轉(zhuǎn)換不夠便捷


          數(shù)值轉(zhuǎn)換工具


          基礎(chǔ)數(shù)據(jù)類型轉(zhuǎn)換是編程語言的剛需。LibConverter提供各類Solidity數(shù)據(jù)基本類型的轉(zhuǎn)換,開發(fā)者可以根據(jù)此工具擴(kuò)展為其他的數(shù)值轉(zhuǎn)換類型和函數(shù)。


          1、數(shù)值類型向下轉(zhuǎn)換,例如uint256轉(zhuǎn)換為uint8。

          function f() public view{    uint256 a = 25;    uint8 b = LibConverter.toUint8(a);}


          2、數(shù)值類型轉(zhuǎn)bytes

          function f() public view{    uint256 a = 25;    bytes memory b = LibConverter.uintToBytes(a);}


          3、bytes轉(zhuǎn)數(shù)值類型

          function f() public view{    bytes memory a = "25";    int b = LibConverter.bytesToInt(a);}


          address轉(zhuǎn)換工具


          address類型是Solidity特有的數(shù)據(jù)類型之一。在日常的程序運(yùn)行邏輯中,常常會(huì)涉及到address與bytes和string類型的互轉(zhuǎn)。LibAddress實(shí)現(xiàn)了上述的轉(zhuǎn)換功能。


          address轉(zhuǎn)bytes

          address addr = 0xE0f5206BBD039e7b0592d8918820024e2a7437b9;bytes memory bs = LibAddress.addressToBytes(addr);


          bytes轉(zhuǎn)address

          bytes memory bs = newbytes(20);address addr = LibAddress.bytesToAddress(bs);


          address轉(zhuǎn)string

          address addr = 0xE0f5206BBD039e7b0592d8918820024e2a7437b9;string memory addrStr = LibAddress.addressToString(addr);


          string轉(zhuǎn)address

          string memory str="0xE0f5206BBD039e7b0592d8918820024e2a7437b9";address addr = LibAddress.stringToAddress(str);


          痛點(diǎn)三:數(shù)組操作不夠豐富


          在Solidity中原生支持的數(shù)組類型,不支持排序、查找、比較、移除、添加、翻轉(zhuǎn)、合并、去重等眾多常用的功能。


          SmartDev-Contract智能合約庫基于動(dòng)態(tài)數(shù)組的結(jié)構(gòu)封裝了“LibArrayForUint256Utils”的常用工具函數(shù)實(shí)現(xiàn)。開發(fā)者也可根據(jù)自身需要的數(shù)據(jù)結(jié)構(gòu),自行封裝相關(guān)的工具類。


          1、添加不重復(fù)的元素

          uint[] private array;function f() public view {    array=new uint[](0);    // array add element 2    LibArrayForUint256Utils.addValue(array,2);    // array: {2}}


          2、合并兩個(gè)數(shù)組

          uint[] private array1;uint[] private array1;function f() public view {    array1=new uint[](2);    array2=new uint[](2);    LibArrayForUint256Utils.extend(array1,array2);    // array1 length 4}


          3、對(duì)數(shù)組去重

          uint[] private array;function f() public view {    array=new uint[](2);    array[0]=2;    array[1]=2;    LibArrayForUint256Utils.distinct(array);    // array: {2}}


          4、對(duì)數(shù)組升序排序

          uint[] private array;function f() public view {    array=new uint[](3);    array[0]=3;    array[1]=2;    array[2]=1;    LibArrayForUint256Utils.qsort(array);      // array: {1,2,3}}


          5、二分查找


          基于已排序的數(shù)組,支持二分查找,提升查找的效率。

          uint[] private array;function f() public view {    array=new uint[](3);    array[0]=3;    array[1]=2;    array[2]=1;    uint256 key=3;    LibArrayForUint256Utils.binarySearch(array,key);      // array: {true, 1}}


          6、刪除元素

          uint[] private array;function f() public view {    array=new uint[](3);    array[0]=3;    array[1]=2;    array[2]=1;    LibArrayForUint256Utils.removeByValue(array,2);      // array: {3, 1}}


          痛點(diǎn)四:不提供字符串內(nèi)置操作


          對(duì)字符串的操作是開發(fā)中較為常見的操作,例如獲取字符串長度、大小寫轉(zhuǎn)換等。在其他開發(fā)語言中,通常會(huì)提供一些內(nèi)置的字符串處理類庫。但Solidity本身沒有提供字符串內(nèi)置操作,因此,這部分需求可通過使用SmartDev-Contract智能合約庫來滿足。


          在SmartDev-Contract智能合約庫中,對(duì)于字符串,我們提供了豐富的功能,這里列舉三個(gè)比較常見的函數(shù)。


          1、獲取字符串長度


          下面的示例中,分別示意了獲取字符串長度、字符串字節(jié)數(shù):

          pragma solidity ^0.4.25;import "./LibString.sol";contract Test {    function f() public{        string memory str = "你好";        uint256 lenOfChars = LibString.lenOfChars(str);        uint256 lenOfBytes = LibString.lenOfBytes(str);        require(lenOfChars == 2);        require(lenOfBytes == 6);    }}


          2、大小寫轉(zhuǎn)換


          下面示例中,將大寫轉(zhuǎn)換為小寫:

          pragma solidity ^0.4.25;import "./LibString.sol";contract Test {    function f() public view returns(string memory)  {        string memory c = LibString.toUppercase("abcd");// Expected to be ABCD        return c;    }}


          3、相等比較

          pragma solidity ^0.4.25;import "./LibString.sol";contract Test {    function f() public view {        bool r = LibString.equal("abcd","abcd");//Expected to be true        require(r);    }}


          4、字符串前綴比較

          pragma solidity ^0.4.25;import "./LibString.sol";contract Test {    function f() public view {        bool r = LibString.startWith("abcd","ab");//Expected to be true        require(r);    }}


          痛點(diǎn)五:高級(jí)數(shù)據(jù)結(jié)構(gòu)不完備


          作為一門面向區(qū)塊鏈的語言,Solidity為了節(jié)省資源,在數(shù)據(jù)結(jié)構(gòu)層面砍掉了許多特性,這使得和常規(guī)語言相比,其在使用上存在較大差異。一方面,Solidity內(nèi)部僅提供了數(shù)組、mapping等數(shù)據(jù)結(jié)構(gòu),如果存在其他需求,需自助實(shí)現(xiàn);另一方面,對(duì)于mapping,其內(nèi)部的鍵僅保存了哈希值,無法獲取鍵的原值。


          綜上所述,我們?cè)赟martDev-Contract智能合約庫中提供了對(duì)數(shù)據(jù)結(jié)構(gòu)的增強(qiáng)支持,以資參考、使用。


          Mapping映射


          在下面這個(gè)例子中,定義了一個(gè)Map,然后向里面存放了三個(gè)鍵值對(duì)。再通過迭代的方式將key取出,存放在事件里。

          pragma solidity ^0.4.25;import "./LibBytesMap.sol";
          contract Test { using LibBytesMap for LibBytesMap.Map; LibBytesMap.Map private map; event Log(bytes key, uint256 index); function f() public { string memory k1 = "k1"; string memory k2 = "k2"; string memory k3 = "k3"; string memory v1 = "v1"; string memory v2 = "v2"; string memory v3 = "v3"; map.put(bytes(k1),bytes(v1)); map.put(bytes(k2),bytes(v2)); map.put(bytes(k3),bytes(v3)); // 開始迭代 uint256 i = map.iterate_start(); while(map.can_iterate(i)){ emit Log(map.getKeyByIndex(i), i); i = map.iterate_next(i); } }}


          address set集合


          作為多數(shù)高級(jí)編程語言標(biāo)配的數(shù)據(jù)結(jié)構(gòu),set是一種集合的數(shù)據(jù)結(jié)構(gòu),其中每個(gè)獨(dú)特屬性的元素都是唯一的。


          SmartDev-Contract智能合約庫依托動(dòng)態(tài)數(shù)組和mapping,實(shí)現(xiàn)了一個(gè)基礎(chǔ)的set集合。此外,由于Solidity不支持泛型機(jī)制,開發(fā)者可以參考此工具,實(shí)現(xiàn)其他元素的set集合。

          pragma solidity ^0.4.25;
          import "./LibAddressSet.sol";
          contract Test { using LibAddressSet for LibAddressSet.AddressSet; LibAddressSet.AddressSet private addressSet; event Log(uint256 size); function testAddress() public { //添加元素; addressSet.add(address(1)); // {1} // 查詢set容器數(shù)量 uint256 size = addressSet.getSize(); require(size == 1); // 獲取指定index的元素 address addr = addressSet.get(0); require(addr == address(1)); // 返回set中所有的元素 addressSet.getAll(); // {0x1} // 判斷元素是否存在 bool contains = addressSet.contains(address(1)); require(contains== true); // 刪除元素 addressSet.remove(address(1)); }}

          除了LibBytesMap和LibAddressSet之外,SmartDev-Contract智能合約庫還包含了堆、棧、單向隊(duì)列、雙向隊(duì)列、雙向鏈表等實(shí)用的數(shù)據(jù)結(jié)構(gòu)。


            業(yè)務(wù)場景的合約模板


          針對(duì)上層的業(yè)務(wù)場景,我們選擇聯(lián)盟鏈落地中最常見、典型的存證和積分業(yè)務(wù)場景,提供智能合約代碼實(shí)例。開發(fā)者可以基于自身的實(shí)際業(yè)務(wù)場景修改智能合約代碼,也可以參考場景中的部分代碼,進(jìn)行擴(kuò)展和裁剪。


          場景一:存證場景


          電子數(shù)據(jù)存證是記錄“用戶身份驗(yàn)證-數(shù)據(jù)創(chuàng)建-存儲(chǔ)-傳輸”全過程的方式,應(yīng)用一系列安全技術(shù)全方位確保電子數(shù)據(jù)的真實(shí)性、完整性、安全性,在司法上具備完整的法律效力。


          使用區(qū)塊鏈智能合約進(jìn)行數(shù)據(jù)存在,具有以下優(yōu)勢:
          • 區(qū)塊鏈技術(shù)完善的防篡改機(jī)制:使用區(qū)塊鏈技術(shù)保全證據(jù),進(jìn)一步加強(qiáng)了證據(jù)不可篡改性。
          • 證據(jù)效力得到機(jī)構(gòu)認(rèn)可:司法機(jī)構(gòu)作為鏈上節(jié)點(diǎn),對(duì)鏈數(shù)據(jù)參與認(rèn)可和簽名,事后可從鏈上確認(rèn)數(shù)據(jù)的真實(shí)有效性。
          • 服務(wù)持續(xù)有效:數(shù)據(jù)被多方共識(shí)上鏈后,即使有部分共識(shí)方退出也不會(huì)造成數(shù)據(jù)的丟失或失效。


          簡要業(yè)務(wù)流程


          • 在存證場景中可以抽象出三類典型用戶:存證方、審核方和取證方。存證方提交需要存證的申請(qǐng)。
          • 審核方基于內(nèi)容,對(duì)存證數(shù)據(jù)進(jìn)行審核和簽名確認(rèn)。實(shí)際業(yè)務(wù)場景中,審核方可能會(huì)涉及投票和多方審核的多簽過程。
          • 當(dāng)存證上鏈后,取證方可隨時(shí)查詢存證者地址、時(shí)間戳和審核詳情等相關(guān)信息進(jìn)行核驗(yàn)。



          合約概要設(shè)計(jì)


          首先,代碼設(shè)計(jì)了邏輯和數(shù)據(jù)層分離。因?yàn)镾olidity智能合約語言沒有獨(dú)立數(shù)據(jù)層,為便于合約后續(xù)擴(kuò)展、升級(jí),需要將邏輯和數(shù)據(jù)層分離,體現(xiàn)在下圖中就是將數(shù)據(jù)層和控制層區(qū)分開。


          其次,依據(jù)合約單一職責(zé)原理,代碼中引入了權(quán)限層。在一條聯(lián)盟鏈上所有節(jié)點(diǎn)可以自由訪問鏈上數(shù)據(jù),智能合約提供了一種修飾器機(jī)制,可控制合約給指定授權(quán)用戶訪問。



          具體的結(jié)構(gòu)如下:
          • Authentication:權(quán)限合約,用于提供基礎(chǔ)的權(quán)限控制功能。
          • EvidenceRepository:存證數(shù)據(jù)倉庫,它繼承了權(quán)限合約,所有存證數(shù)據(jù)都被保存到數(shù)據(jù)合約里。這樣可以起到統(tǒng)一存儲(chǔ)、統(tǒng)一管理的效果。
          • RequestRepository:請(qǐng)求數(shù)據(jù)倉庫,存儲(chǔ)了存證數(shù)據(jù)和投票請(qǐng)求信息等。存證方開始提交存證數(shù)據(jù)并不會(huì)直接被寫入存證倉庫中,而是經(jīng)過審核方簽名完成后才會(huì)真正提交,審核方可以為多方。
          • EvidenceController:控制器,引入了兩個(gè)數(shù)據(jù)倉庫合約,可以完成所有用戶接口的交互。包含了創(chuàng)建存證請(qǐng)求,審核人根據(jù)請(qǐng)求進(jìn)行投票的功能。


          場景二:積分場景


          區(qū)塊鏈積分場景是指多個(gè)獨(dú)立對(duì)等的零售商組成聯(lián)盟,利用公眾聯(lián)盟鏈為消費(fèi)者提供真正意義上的全渠道綜合消費(fèi)體驗(yàn)。


          在積分場景中引入?yún)^(qū)塊鏈,具有以下優(yōu)勢:
          • 區(qū)塊鏈技術(shù)可以增加品牌曝光度:多個(gè)機(jī)構(gòu)組成積分聯(lián)盟,積分可有效通兌,實(shí)現(xiàn)客戶資源引流,提升營銷效果。
          • 保證積分安全性:所有積分的生成和流轉(zhuǎn)過程保存到鏈上,防止商戶篡改和抵賴。
          • 提升用戶體驗(yàn):不同商戶和用戶之間實(shí)現(xiàn)積分流轉(zhuǎn)、互通,更加便利。


          簡要業(yè)務(wù)流程


          基于區(qū)塊鏈技術(shù),多個(gè)商家組成積分聯(lián)盟,實(shí)現(xiàn)積分通存通兌、客戶資源相互引流等。管理者部署和管理合約。商家有發(fā)行積分、拉入其他商家、撤銷發(fā)行者身份的權(quán)限。消費(fèi)者有開戶、銷戶、消費(fèi)積分和積分轉(zhuǎn)賬的權(quán)限。



          合約概要設(shè)計(jì)


          在存證合約中,我們引入了數(shù)據(jù)和邏輯分離的思想;在積分合約中,我們引入管理、數(shù)據(jù)和邏輯分離的思想。


          引入了管理合約后,就實(shí)現(xiàn)了類似控制反轉(zhuǎn)的效果,控制合約和數(shù)據(jù)合約都由管理合約來創(chuàng)建;同時(shí),管理合約還可以隨時(shí)設(shè)置數(shù)據(jù)合約中控制合約的地址。這樣,控制合約就可以隨時(shí)實(shí)現(xiàn)平滑的業(yè)務(wù)邏輯升級(jí);將管理合約分離出來,還有利于鏈上權(quán)限治理。



          • Admin:管理并生成合約,控制訪問Data合約的地址
          • Controller:合約層對(duì)外暴露服務(wù)的控制器
          • Data:存儲(chǔ)業(yè)務(wù)合約相關(guān)的數(shù)據(jù)
          • BasicAuth、IssuerRole:權(quán)限、角色的工具合約
          • LibRoles、LibSafeMath:權(quán)限mapping,數(shù)值計(jì)算的庫


            即刻體驗(yàn)


          上述優(yōu)化及功能所涉及的最新代碼和技術(shù)文檔已同步更新,歡迎體驗(yàn)和 star 支持。如需咨詢技術(shù)問題,歡迎關(guān)注本公眾號(hào),對(duì)話框回復(fù)【小助手】進(jìn)技術(shù)交流群。


          文檔地址: 

          https://smartdev-doc.readthedocs.io/zh_CN/latest/docs/WeBankBlockchain-SmartDev-Contract/index.html

          GitHub代碼庫地址:

          https://github.com/WeBankBlockchain/SmartDev-Contract

          gitee代碼庫地址:

          https://gitee.com/WeBankBlockchain/SmartDev-Contract


          歡迎參與WeBankBlockchain的社區(qū)建設(shè):

          • 如項(xiàng)目對(duì)您有幫助,歡迎點(diǎn)亮我們的小星星(點(diǎn)擊項(xiàng)目左上方Star按鈕)。

          • 歡迎提交代碼(Pull requests)。

          • 提問和提交BUG。

          • 如果發(fā)現(xiàn)代碼存在安全漏洞,可通過以下地址上報(bào):

            https://security.webank.com/




          瀏覽 76
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  校花被日视频网站 | re久久6热 | 国产操逼免费网站 | 欧美日韩免费A片 | 98成人网 |