主頁 > 區塊鏈 > 基于 sCrypt 合約開發一個完整的 dApp:井字棋游戲

基于 sCrypt 合約開發一個完整的 dApp:井字棋游戲

2021-04-04 14:29:21 區塊鏈

在我們之前的博客中,介紹了如何使用 sCrypt 來撰寫位元幣智能合約,但是作為剛入門的開發者,你可能對如何使用 sCrypt 來構建 dApp 更加感興趣,接下來我們將教大家如何使用 sCrypt 一步一步地構建一個井字棋 dApp.

該應用程式非常簡單,它所做的就是使用兩個玩家(分別是 Alice 和 Bob)的公鑰哈希,初始化合約,各自下注相同的金額鎖定到合約中,只有贏得那個人可以取走合約里面的錢,如果最后沒有人贏,則兩個玩家各自可以取走一半的錢,目標不僅是對應用程式進行編碼,主要是學習如何對其進行編譯,測驗,部署和互動的程序,

我們將逐步完成構建全堆疊去中心化應用程式的整個程序,包括:

  1. 撰寫合約
  2. 測驗合約
  3. 將 web app 集成合約

搭建開發環境

  1. 安裝 sCrypt IDE,見 sCrypt 開發工具篇 - Visual Studio Code 插件
  2. 安裝 nodejs, version >= 12
  3. 安裝 Typescript

搭建開發環境非常簡單方便,接下來我們用 create-react-app 來創建一個 react app, 執行 npx create-react-app tic-tac-toe,然后用 vscode 打開我們剛剛創建的代碼工程,并在根目錄下創建一個contracts 目錄,用來存放我們的合約代碼,創建一個 test 目錄,用來存放合約的測驗代碼,你將看到以下目錄結構,

目錄

使用 sCrypt 撰寫 tic-tac-toe 合約

我們將使用 sCrypt 編程語言來撰寫一個名為 TicTacToe 的合約, TicTacToe 合約主要實作原理是將游戲的狀態存盤在合約中,這在之前的文章已經詳細介紹過了

游戲狀態由以下組成:

  1. turn: 輪到誰下棋, 0 表示輪到 Alice, 1 表示輪到 Bob, 長度為 1 byte
  2. board: 記錄棋盤當前的狀態,每個位元組代表棋盤的一個位置,0 表示空,1 表示 ALICE,2 表示 BOB,長度為 9 byte

import "util.scrypt";

contract TicTacToe {
    PubKey alice;
    PubKey bob;

    static const int TURNLEN = 1;
    static const int BOARDLEN = 9;
    static const bytes EMPTY = b'00';
    static const bytes ALICE = b'01';
    static const bytes BOB = b'02';

    public function move(int n, Sig sig, int amount, SigHashPreimage txPreimage) {
        require(Tx.checkPreimage(txPreimage));
        require(n >= 0 && n < BOARDLEN);

        bytes scriptCode = Util.scriptCode(txPreimage);
        int scriptLen = len(scriptCode);

        int boardStart = scriptLen - BOARDLEN;
        // state: turn (1 byte) + board (9 bytes)
        int turn = unpack(scriptCode[boardStart - TURNLEN : boardStart]);
        bytes board = scriptCode[boardStart : ];

        // not filled
        require(Util.getElemAt(board, n) == EMPTY);

        bytes play = turn == 0 ? ALICE : BOB;
        PubKey player = turn == 0 ? this.alice : this.bob;

        // ensure it's player's turn
        require(checkSig(sig, player));
        // make the move
        board = Util.setElemAt(board, n, play);

        bytes outputs = b'';
        if (this.won(board, play)) {
            // winner takes all
            bytes outputScript = Util.pubKeyToP2PKH(player);
            bytes output = Util.buildOutput(outputScript, amount);
            outputs = output;
        }
        else if (this.full(board)) {
            // draw: equally split, i.e., both outputs have the same amount
            bytes aliceScript = Util.pubKeyToP2PKH(this.alice);
            bytes aliceOutput = Util.buildOutput(aliceScript, amount);

            bytes bobScript = Util.pubKeyToP2PKH(this.bob);
            bytes bobOutput = Util.buildOutput(bobScript, amount);

            outputs = aliceOutput + bobOutput;
        } else {
            // update state: next turn & next board
            bytes scriptCode_ = scriptCode[ : scriptLen - BOARDLEN - TURNLEN] + num2bin(1 - turn, TURNLEN) + board;
            bytes output = Util.buildOutput(scriptCode_, amount);
            outputs = output;
        }

        require(hash256(outputs) == Util.hashOutputs(txPreimage));
    }

    // does play win after current move?
    function won(bytes board, bytes play) : bool {
        // three in a row, a column, or a diagnoal
        int[8][3] lines = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],
        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],
        [0, 4, 8],
        [2, 4, 6]
        ];

        bool anyLine = false;
        loop (8) : i {
            bool line = true;
            loop (3) : j {
                line = line && Util.getElemAt(board, lines[i][j]) == play;
            }

            anyLine = anyLine || line;
        }

        return anyLine;
    }

    // is board full?
    function full(bytes board) : bool {
        bool full = true;

        loop (BOARDLEN) : i {
            full = full && Util.getElemAt(board, i) != EMPTY;
        }

        return full;
    }
}

合約中有 3 個函式:

  1. move 愛麗絲(Alice)和鮑勃(Bob)各自將 X 個位元幣鎖定在包含上述合同的一個 UTXO 中, 接下來,他們通過呼叫公共函式move()交替玩游戲
  2. won 檢查是否有玩家已經贏得比賽,他將能取走所有合約鎖定的賭注
  3. full 如果棋盤,沒人贏得比賽,則兩個人平分賭注

測驗合約

接下來我們用 Javascript 撰寫合約的單元測驗,以確保合約在上線部署之前能夠按預期作業, 通過sCrypt 測驗框架,我們可以模擬呼叫 move() 并斷言游戲狀態


const Tictactoe = buildContractClass(runCompile('tictactoe.scrypt'));

game = new Tictactoe(new PubKey(toHex(publicKey1)), new PubKey(toHex(publicKey2)));

let state = new Bytes('00000000000000000000').toASM();
game.setDataPart(state)

describe('Test sCrypt contract Tictactoe In Javascript', () => {
  let result, preimage, sig, prevLockingScript


  it('Alice places an X at 0-th cell', () => {

    prevLockingScript = game.lockingScript.toASM();

    let newState = new Bytes('01010000000000000000').toASM();

    const tx = newTx();
    const newLockingScript = [game.codePart.toASM(), newState].join(' ');


    tx.addOutput(new bsv.Transaction.Output({
      script: bsv.Script.fromASM(newLockingScript),
      satoshis: 10000
    }))

    preimage = getPreimage(tx, prevLockingScript, inputSatoshis);

    sig = signTx(tx, privateKey1, prevLockingScript, inputSatoshis)

    const context = { tx, inputIndex, inputSatoshis }

    result = game.move(0, new Sig(toHex(sig)), 10000, preimage).verify(context)
    expect(result.success, result.error).to.be.true;

    game.setDataPart(newState)

  });
}

集成合約

1. 部署合約

我們將復用 官方 React 教程 中現有的 tic-tac-toe 專案,如果您有前端開發的經驗,這應該看起來很熟悉,我們將專注于集成 dApp 的智能合約部分,對于與位元幣區塊鏈的所有互動,我們使用 whatsonchain 提供的 API,

  1. 通過右鍵單擊 編譯 來編譯我們的合約,將會輸出 tictactoe_desc.json,這包含有關我們合約的所有內容,將其拷貝到 public 目錄中,以便我們的能從前端頁面加載到該檔案,
    在這里插入圖片描述

  2. 我們需要先實作一個測驗網的錢包,我們將錢包的介面定義在wallet.ts 中, 包括以下介面:

    
      //Dapp use this api to connect to the wallet.
      abstract requestAccount(name: string, permissions: string[]): Promise<Account>;
    
      //get wallet balance
      abstract getbalance(): Promise<number>;
    
      //sign raw transaction, returns unlockscript of the p2pkh input if success
      abstract signRawTransaction(tx: Tx, inputIndex: number, sigHashType: SignType
      ): Promise<string>;
    
      //get signature for special input
      abstract getSignature(tx: Tx, inputIndex: number, sigHashType: SignType
      ): Promise<string>;
    
      //send raw transaction, returns transaction hash if success
      abstract sendRawTransaction(rawTx: string): Promise<string>;
    
      //returns array of unspent transaction outputs, which total amount is more than the minAmount argument.
      abstract listUnspent(minAmount: number, options?: {
        purpose?: string
      }): Promise<UTXO[]>;
    
      //returns a new Bitcoin address, for receiving change.
      abstract getRawChangeAddress(options?: {
        purpose?: string
      }): Promise<string>;
    
      //returns a public key
      abstract getPublicKey(options?: {
        purpose?: string
      }): Promise<string>;
    
    

    localwallet.ts 則是我們的具體實作,

    我們創建 wallet.js react 組件,并在頁面中繪制出來
    在這里插入圖片描述

    這樣 Bob 和 Alice 就能往 dApp 充位元幣了,

  3. 資金準備就緒后,就能使用步驟 1 中的 tictactoe_desc.json 以及 Alice 和 Bob 的公鑰來實體化合約了,

    async function fetchContract(alicePubKey, bobPubKey) {
      let { contractClass: TictactoeContractClass } = await web3.loadContract(
        "/tic-tac-toe/tictactoe_desc.json"
      );
    
      let instance = newCall(TictactoeContractClass, [
        new PubKey(toHex(alicePubKey)),
        new PubKey(toHex(bobPubKey)),
      ]);
      instance.setDataPart("00000000000000000000");
      updateContractInstance(instance);
      console.log("fetchContract successfully");
      return instance;
    }
    
  4. 合約實體化后,可以通過合約實體來構建交易了,Alice 和 Bob 分別提供一個 Input,同時我們給他添加一個對應的 Output 用于找零,最后我們構建出來一個包含 2 個 Input 和 3 個 Output 的交易,

     static async buildDeployTx(contract: AbstractContract, amountInContract: number, alicePrivateKey: string, bobPrivateKey: string): Promise<Tx> {
    
         let aliceWallet = new LocalWallet(NetWork.Testnet, alicePrivateKey);
         let bobWallet = new LocalWallet(NetWork.Testnet, bobPrivateKey);
    
         const aliceChangeAddress = await aliceWallet.getRawChangeAddress();
         const bobChangeAddress = await bobWallet.getRawChangeAddress();
    
         const tx: Tx = {
           inputs: [],
           outputs: []
         };
    
         tx.outputs.push({
           script: contract.lockingScript.toHex(),
           satoshis: amountInContract * 2
         });
    
         const minAmount = amountInContract + FEE;
    
         return aliceWallet.listUnspent(minAmount, {
           purpose: 'change'
         }).then(async (utxos: UTXO[]) => {
    
           if (utxos.length === 0) {
             throw new Error('no utxos');
           }
    
           //add input which using utxo from alice
           tx.inputs.push(
             {
               utxo: utxos[0],
               script: '',
               sequence: 0
             }
           );
    
           const changeAmount = utxos[0].satoshis - amountInContract - FEE;
    
           if (changeAmount <= 0) {
             throw new Error('fund is not enough');
           }
    
           //add alice change output
    
           tx.outputs.push(
             {
               script: bsv.Script.buildPublicKeyHashOut(aliceChangeAddress).toHex(),
               satoshis: changeAmount
             }
           );
    
           return tx;
         }).then(tx => {
           return bobWallet.listUnspent(minAmount, {
             purpose: 'change'
           }).then(async (utxos: UTXO[]) => {
    
             if (utxos.length === 0) {
               throw new Error('no utxos');
             }
    
             //add input which using utxo from bob
             tx.inputs.push(
               {
                 utxo: utxos[0],
                 script: '',
                 sequence: 0
               }
             );
    
             const changeAmount = utxos[0].satoshis - amountInContract - FEE;
    
             if (changeAmount <= 0) {
               throw new Error('fund is not enough');
             }
    
             //add bob change output
             tx.outputs.push(
               {
                 script: bsv.Script.buildPublicKeyHashOut(bobChangeAddress).toHex(),
                 satoshis: changeAmount
               }
             );
    
             return tx;
    
           })
         }).then(tx => {
           //alice sign
           return aliceWallet.signRawTransaction(tx, 0, SignType.ALL).then(unlockscript => {
             tx.inputs[0].script = unlockscript;
             return tx;
           })
         }).then(tx => {
           //bob sign
           return bobWallet.signRawTransaction(tx, 1, SignType.ALL).then(unlockscript => {
             tx.inputs[1].script = unlockscript;
             return tx;
           })
         })
       }
    
  5. 交易構建完成后,分別由 Alice 和 Bob 進行簽名,然后廣播,從而完成合約的部署,我們嘗試運行 npm start ,并用瀏覽器打開http://localhost:3000, 嘗試開始游戲,如果順利,我們將會看到部署成功的交易,嘗試打開瀏覽一下交易:

在這里插入圖片描述

2. 呼叫合約

接下來就是開始下棋了,每下一步棋,就是對合約的一次呼叫,并觸發合約狀態的改變,呼叫合約需要構建一個符合合約規則的交易,首先需要計算合約的新狀態,同時,我們可以在 dApp 通過棋盤狀態推演出來誰輸誰贏,這樣就能根據結果來構建交易的輸出,下面就是推演結果和構建呼叫合約交易的代碼,

  1. 計算合約的新狀態

    
      calculateNewState(squares) {
        return (!this.state.xIsNext ? '00' : '01') + squares.map(square => {
    
          if (square && square.label === 'X') {
            return '01'
          } else if (square && square.label === 'O') {
            return '02'
          } else {
            return '00';
          }
        }).join('');
      }
    
    
  2. 構建呼叫合約的交易

    
    async buildCallContractTx(i, newState, squares, history) {
        let newLockingScript = "";
        let winner = calculateWinner(squares).winner;
        const FEE = 3000;
        let outputs = [];
        let amount = this.props.game.lastUtxo.satoshis - FEE;
        if (winner) {
          // winner is current player
    
          let address = await web3.wallet.getRawChangeAddress();
    
          newLockingScript = bsv.Script.buildPublicKeyHashOut(address).toHex();
    
          outputs.push({
            satoshis: amount,
            script: newLockingScript
          })
    
        } else if (history.length >= 9) {
    
          const aliceAddress = new bsv.PublicKey(this.props.game.alicePubKey, {
            network: bsv.Networks.testnet
          });
          const bobAddress = new bsv.PublicKey(this.props.game.bobPubKey, {
            network: bsv.Networks.testnet
          });
    
          //no body win
          const aliceLockingScript = bsv.Script.buildPublicKeyHashOut(aliceAddress.toAddress(bsv.Networks.testnet)).toHex();
          const bobLockingScript = bsv.Script.buildPublicKeyHashOut(bobAddress.toAddress(bsv.Networks.testnet)).toHex();
          amount = (this.props.game.lastUtxo.satoshis - FEE) / 2;
    
          outputs.push({
            satoshis: amount,
            script: aliceLockingScript
          })
    
          outputs.push({
            satoshis: amount,
            script: bobLockingScript
          })
    
        } else {
          //next
          newLockingScript = [this.props.contractInstance.codePart.toHex(), bsv.Script.fromASM(newState).toHex()].join('');
          outputs.push({
            satoshis: amount,
            script: newLockingScript
          })
        }
    
    
        if (outputs[0].satoshis <= 0) {
          alert(`fund in contract is too low `)
          return undefined;
        }
    
    
        let tx = {
          inputs: [{
            utxo: this.props.game.lastUtxo,
            sequence: 0,
            script: ""
          }],
          outputs: outputs
        }
    
        let preimage = getPreimage(tx);
    
        let sig = await web3.wallet.getSignature(tx, 0, SignType.ALL, true);
    
        let unlockScript = this.props.contractInstance.move(i, new Sig(toHex(sig)), amount, preimage).toHex();
    
        tx.inputs[0].script = unlockScript;
    
        return tx;
      }
    
    

    上面 Input 對應的 script 默認是空的,也就是構建的交易并沒包含解鎖腳本,我們需要計算并填充解鎖腳本,才能構成一個完整的交易, 我們知道 sCrypt 合約 的 public 方法的引數就是對應的解鎖腳本,TicTacToe 合約的 move 函式有 4 個引數:

    1. n 棋盤位置
    2. amount 合約花費后剩下的余額
    3. txPreimage 交易原象, 如果您對這個引數不了解,可以查看深入學習位元幣腳本之 OP_PUSH_TX
    4. sig 對交易的簽名

    前面 2 個參我們都可以在 dApp 端計算, txPreimage 由于未簽名的交易模板已經構建好,我們也可以直接在 dApp 端直接呼叫 getPreimage 方法計算,sig 的計算涉及到私鑰,我需要使用錢包的 getSignature 方法,

    當 4 個解鎖引數我們都計算好之后,我們可以呼叫合約實體的 move 方法來組裝解鎖腳本得到 unlockScript

        ...
    
        let unlockScript = this.props.contractInstance.move(i, new Sig(toHex(sig)), amount, preimage).toHex();
        tx.inputs[0].script = unlockScript; // 填充解鎖腳本后,交易才算是構建完整了
    
        ...
    
  3. 交易構建完成了,接下來通過 API 將交易廣播到區塊鏈上,同時我們更新合約最新的 UTXO, 以便下次呼叫合約時候可以直接使用,

        web3.sendTx(tx).then(txid => {
           squares[i].tx = txid;
           squares[i].n = history.length;
           let gameState = {
             history: history.concat([
               {
                 squares,
                 currentLocation: getLocation(i),
                 stepNumber: history.length,
               },
             ]),
             xIsNext: !this.state.xIsNext,
             currentStepNumber: history.length,
           };
    
           server.saveGame(Object.assign({}, this.props.game, {
             gameState: gameState,
             lastUtxo: {
               txHash: txid,
               outputIndex: 0,
               satoshis: tx.outputs[0].satoshis,
               script: tx.outputs[0].script
             }
           }), 'next')
    
           this.setState(gameState);
    
       }).catch(e => {
         ...
       })
    

    注意,由于這里我們使用的 localstorage 來模擬服務器通信,所以 alice 更新 UTXO 后,bob 也就能獲取到對應的最新的 UTXO,在實際的生產環境中,合約的最新的 utxo 需要通過其它方法來獲取,

至此,我們完成了 TicTacToe 小游戲下棋動作和合約呼叫的系結,玩家的每個下棋動作,都產生一個區塊鏈上對應的 transaction 與之對應,

總結

恭喜你! 您剛剛在位元幣上構建了第一個全堆疊 dApp, 現在,您可以玩井字游戲或在位元幣上構建您自己喜歡的游戲,現在是時候喝些香檳了,或者打開下方連接和小伙伴來一場比賽!

  • 本文演示的游戲可以在 這里試玩(注意僅支持測驗網,請勿使用主網上的錢包
  • 本文使用的所有代碼均源自這個 Github Repo ,歡迎大家加星收藏,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/272297.html

標籤:區塊鏈

上一篇:以太坊或ERC20轉賬查詢(Java版本)

下一篇:數字簽名與數字證書在基于非對稱密鑰加密的通信工程中的應用

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • JAVA使用 web3j 進行token轉賬

    最近新學習了下區塊鏈這方面的知識,所學不多,給大家分享下。 # 1. 關于web3j web3j是一個高度模塊化,反應性,型別安全的Java和Android庫,用于與智能合約配合并與以太坊網路上的客戶端(節點)集成。 # 2. 準備作業 jdk版本1.8 引入maven <dependency> < ......

    uj5u.com 2020-09-10 03:03:06 more
  • 以太坊智能合約開發框架Truffle

    前言 部署智能合約有多種方式,命令列的瀏覽器的渠道都有,但往往跟我們程式員的風格不太相符,因為我們習慣了在IDE里寫了代碼然后打包運行看效果。 雖然現在IDE中已經存在了Solidity插件,可以撰寫智能合約,但是部署智能合約卻要另走他路,沒辦法進行一個快捷的部署與測驗。 如果團隊管理的區塊節點多、 ......

    uj5u.com 2020-09-10 03:03:12 more
  • 谷歌二次驗證碼成為區塊鏈專用安全碼,你怎么看?

    前言 谷歌身份驗證器,前些年大家都比較陌生,但隨著國內互聯網安全的加強,它越來越多地出現在大家的視野中。 比較廣泛接觸的人群是國際3A游戲愛好者,游戲盜號現象嚴重+國外賬號安全應用廣泛,這類游戲一般都會要求用戶系結名為“兩步驗證”、“雙重驗證”等,平臺一般都推薦用谷歌身份驗證器。 后來區塊鏈業務風靡 ......

    uj5u.com 2020-09-10 03:03:17 more
  • 密碼學DAY1

    目錄 ##1.1 密碼學基本概念 密碼在我們的生活中有著重要的作用,那么密碼究竟來自何方,為何會產生呢? 密碼學是網路安全、資訊安全、區塊鏈等產品的基礎,常見的非對稱加密、對稱加密、散列函式等,都屬于密碼學范疇。 密碼學有數千年的歷史,從最開始的替換法到如今的非對稱加密演算法,經歷了古典密碼學,近代密 ......

    uj5u.com 2020-09-10 03:03:50 more
  • 密碼學DAY1_02

    目錄 ##1.1 ASCII編碼 ASCII(American Standard Code for Information Interchange,美國資訊交換標準代碼)是基于拉丁字母的一套電腦編碼系統,主要用于顯示現代英語和其他西歐語言。它是現今最通用的單位元組編碼系統,并等同于國際標準ISO/IE ......

    uj5u.com 2020-09-10 03:04:50 more
  • 密碼學DAY2

    ##1.1 加密模式 加密模式:https://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html ECB ECB : Electronic codebook, 電子密碼本. 需要加密的訊息按照塊密碼的塊大小被分為數個塊,并對每個塊進 ......

    uj5u.com 2020-09-10 03:05:42 more
  • NTP時鐘服務器的特點(京準電子)

    NTP時鐘服務器的特點(京準電子) NTP時鐘服務器的特點(京準電子) 京準電子官V——ahjzsz 首先對時間同步進行了背景介紹,然后討論了不同的時間同步網路技術,最后指出了建立全球或區域時間同步網存在的問題。 一、概 述 在通信領域,“同步”概念是指頻率的同步,即網路各個節點的時鐘頻率和相位同步 ......

    uj5u.com 2020-09-10 03:05:47 more
  • 標準化考場時鐘同步系統推進智能化校園建設

    標準化考場時鐘同步系統推進智能化校園建設 標準化考場時鐘同步系統推進智能化校園建設 安徽京準電子科技官微——ahjzsz 一、背景概述隨著教育事業的快速發展,學校建設如雨后春筍,隨之而來的學校教育、管理、安全方面的問題成了學校管理人員面臨的最大的挑戰,這些問題同時也是學生家長所擔心的。為了讓學生有更 ......

    uj5u.com 2020-09-10 03:05:51 more
  • 位元幣入門

    引言 位元幣基本結構 位元幣基礎知識 1)哈希演算法 2)非對稱加密技術 3)數字簽名 4)MerkleTree 5)哪有位元幣,有的是UTXO 6)位元幣挖礦與共識 7)區塊驗證(共識) 總結 引言 上一篇我們已經知道了什么是區塊鏈,此篇說一下區塊鏈的第一個應用——位元幣。其實先有位元幣,后有的區塊 ......

    uj5u.com 2020-09-10 03:06:15 more
  • 北斗對時服務器(北斗對時設備)電力系統應用

    北斗對時服務器(北斗對時設備)電力系統應用 北斗對時服務器(北斗對時設備)電力系統應用 京準電子科技官微(ahjzsz) 中國北斗衛星導航系統(英文名稱:BeiDou Navigation Satellite System,簡稱BDS),因為是目前世界范圍內唯一可以大面積提供免費定位服務的系統,所以 ......

    uj5u.com 2020-09-10 03:06:20 more
最新发布
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:46:47 more
  • Hyperledger Fabric 使用 CouchDB 和復雜智能合約開發

    在上個實驗中,我們已經實作了簡單智能合約實作及客戶端開發,但該實驗中智能合約只有基礎的增刪改查功能,且其中的資料管理功能與傳統 MySQL 比相差甚遠。本文將在前面實驗的基礎上,將 Hyperledger Fabric 的默認資料庫支持 LevelDB 改為 CouchDB 模式,以實作更復雜的資料... ......

    uj5u.com 2023-04-16 07:28:31 more
  • .NET Core 波場鏈離線簽名、廣播交易(發送 TRX和USDT)筆記

    Get Started NuGet You can run the following command to install the Tron.Wallet.Net in your project. PM> Install-Package Tron.Wallet.Net 配置 public reco ......

    uj5u.com 2023-04-14 08:08:00 more
  • DKP 黑客分析——不正確的代幣對比率計算

    概述: 2023 年 2 月 8 日,針對 DKP 協議的閃電貸攻擊導致該協議的用戶損失了 8 萬美元,因為 execute() 函式取決于 USDT-DKP 對中兩種代幣的余額比率。 智能合約黑客概述: 攻擊者的交易:0x0c850f,0x2d31 攻擊者地址:0xF38 利用合同:0xf34ad ......

    uj5u.com 2023-04-07 07:46:09 more
  • Defi開發簡介

    Defi開發簡介 介紹 Defi是去中心化金融的縮寫, 是一項旨在利用區塊鏈技術和智能合約創建更加開放,可訪問和透明的金融體系的運動. 這與傳統金融形成鮮明對比,傳統金融通常由少數大型銀行和金融機構控制 在Defi的世界里,用戶可以直接從他們的電腦或移動設備上訪問廣泛的金融服務,而不需要像銀行或者信 ......

    uj5u.com 2023-04-05 08:01:34 more
  • solidity簡單的ERC20代幣實作

    // SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.7.0 <0.9.0; import "hardhat/console.sol"; //ERC20 同質化代幣,每個代幣的本質或性質都是相同 //ETH 是原生代幣,它不是ERC20代幣, ......

    uj5u.com 2023-03-21 07:56:29 more
  • solidity 參考型別修飾符memory、calldata與storage 常量修飾符C

    在solidity語言中 參考型別修飾符(參考型別為存盤空間不固定的數值型別) memory、calldata與storage,它們只能修飾參考型別變數,比如字串、陣列、位元組等... memory 適用于方法傳參、返參或在方法體內使用,使用完就會清除掉,釋放記憶體 calldata 僅適用于方法傳參 ......

    uj5u.com 2023-03-08 07:57:54 more
  • solidity注解標簽

    在solidity語言中 注釋符為// 注解符為/* 內容*/ 或者 是 ///內容 注解中含有這幾個標簽給予我們使用 @title 一個應該描述合約/介面的標題 contract, library, interface @author 作者的名字 contract, library, interf ......

    uj5u.com 2023-03-08 07:57:49 more
  • 評價指標:相似度、GAS消耗

    【代碼注釋自動生成方法綜述】 這些評測指標主要來自機器翻譯和文本總結等研究領域,可以評估候選文本(即基于代碼注釋自動方法而生成)和參考文本(即基于手工方式而生成)的相似度. BLEU指標^[^?88^^?^]^:其全稱是bilingual evaluation understudy.該指標是最早用于 ......

    uj5u.com 2023-02-23 07:27:39 more
  • 基于NOSTR協議的“公有制”版本的Twitter,去中心化社交軟體Damus

    最近,一個幽靈,Web3的幽靈,在網路游蕩,它叫Damus,這玩意詮釋了什么叫做病毒式營銷,滑稽的是,一個Web3產品卻在Web2的產品鏈上瘋狂傳銷,各方大佬紛紛為其背書,到底發生了什么?Damus的葫蘆里,賣的是什么藥? 注冊和簡單實用 很少有什么產品在用戶注冊環節會有什么噱頭,但Damus確實出 ......

    uj5u.com 2023-02-05 06:48:39 more