有很多不同的解決方案可以創建Dapp,這些Dapp可以接觸到數千甚至數百萬實時用戶,如Plasam和狀態通道。在本文中,您將了解狀態通道如何工作,以及如何在以太坊中創建可擴展的Dapp。
什么是狀態通道?
狀態通道是一種2層擴展解決方案,可以用于創建Dapp和智能合約,幾乎可以被數百萬用戶實時使用。它們通過在2個或多個用戶之間啟動多個通道來工作,并執行事務的信息交換加密的簽名消息。
它們被稱為“狀態”,是因為每個交互都必須具有可以更新的狀態。例如游戲得分或銀行余額。
我們需要什么來建立一個狀態通道?
1. 一個狀態通道需要至少2個或多個用戶同時交互才能打開。類似即時聊天工具一樣。
2. 具有打開和關閉狀態通道邏輯的智能合約。
3. 如果將在游戲中使用狀態通道,則兩個用戶都需要進行托管。在打開狀態通道時,以太網中的托管都將存儲在智能合約中。
4. 一個javascript應用程序,它將生成簽名消息,這些消息將在用戶之間的鏈外交換。
5. Metamask或用于簽名消息的類似工具。簽名消息不需要損耗gas,并會立即執行。兩個用戶都需要對消息進行簽名,以保證tehy是生成此類事務的人。
6. 通過電子郵件或任何外部應用程序交換這些簽名郵件。
狀態通道如何工作?
狀態通道設置起來有點復雜,因為你必須確保兩個玩家都受到保護,以防出現任何問題,這就是為什么我們需要一個智能合約。步驟如下:
1. 在2個用戶之間的狀態通道中,第一個用戶部署智能合約,該合約將“打開”該通道。
2. 第二個執行智能合約的以“加入”功能進入該狀態通道。
3. 然后他們可以開始為應用程序交換簽名的消息。兩個用戶都可以訪問自定義javascript應用程序,以生成鏈外消息,這些消息包含他們在智能合約中可以執行的信息。
4. 事務的速度取決于每個用戶創建和簽署這些消息的速度。他們需要不停地交換信息,不停地玩,直到他們決定游戲結束。
5. 當他們結束游戲后,他們中的任何一人都可以進入智能合約并執行一個功能來完成它,這將開始“協商”階段。
6. 在此階段,兩個用戶都有超時1天的時間將最新的2條消息上傳到智能合約。智能合約檢查最新消息并釋放資金以基于該信息結束游戲。每條消息都包含先前交互的結果,因此只檢查最新的消息是安全的。
在本文中,我將向您展示如何在兩個用戶之間為一個以太坊游戲創建一個狀態通道。請記住,狀態通道可以用于具有“狀態”或“計數器”的任何類型的應用程序。這就是為什么狀態通道應用于游戲是非常理想的。因為你可以追蹤每一場比賽的勝利者,所以每一場比賽都有一個狀態可以更新。
我們將創建一個骰子游戲,玩家1選擇指定自己想要的數字,玩家2必須猜測該數字才能獲勝。他們可以任意進行游戲,而無需在區塊鏈上執行交易。我們還有一個Web應用程序來顯示游戲界面。
這是我們要創建Dapp的索引:
1. 創建可視化Web應用程序。它將用作交換狀態通道的簽名消息的媒介。
2. 創建簽名和加密消息所需的功能。
3. 創建智能合約。
1.創建可視化Web應用程序
在開始編寫代碼之前,我想確保我們弄清楚了Web應用程序的完整細節。它看起來怎么樣,關注的焦點是什么。
在這種情況下,我們希望為兩個玩家展示類似的東西。玩家1將看到骰子的6個面并且他將必須選擇哪個面為結果展示出來,然后第二個玩家,還必須在這些面之間進行選擇并且他將能夠看到結果。
所以框架是這樣的:
1、玩家1進入應用程序,點擊一個按鈕說“開始新游戲”,然后他做一個metamask事務來部署和設置智能合約。他收到一個智能合約地址,可以發送給其他玩家開始游戲。
2、玩家2進入應用程序,點擊一個顯示“加入現有游戲”的按鈕,其中包含從玩家1收到的合同地址,然后他進行metamask交易以設置現有游戲并發送一個托管。
那么讓我們開始,在Web應用程序的中間創建一個帶有2個按鈕的框。創建一個名為dice的文件夾和一個名為index.html的文件。這是代碼:
《!DOCTYPE html》
《html lang=“en” dir=“ltr”》
《head》
《meta charset=“utf-8”》
《title》Dice ethereum game《/title》
《/head》
《body》
《div class=“main-content”》
《button》Start new game《/button》
《button》Join existing game《/button》
《/div》
《/body》
《/html》
在這段代碼中,我剛創建了一個基本的HTML結構,其中包含一個包含按鈕和標題的div。請注意,div有一個名為main-content的類,我們稍后會使用它。
讓我們用一些css修飾一下界面。使用以下代碼創建一個名為index.css的文件:
body {
font-family: sans-serif;
}
.main-content {
margin: auto;
max-width: 500px;
background-color: whitesmoke;
padding: 50px;
border-radius: 10px;
display: grid;
grid-template-rows: 1fr 1fr;
grid-template-columns: 1fr 1fr;
grid-column-gap: 10px;
}
.main-content h1 {
grid-column: 1 / span 2;
}
.main-content button {
border: none;
color: white;
background-color: #007dff;
padding: 20px;
border-radius: 5px;
}
.main-content button:hover {
opacity: 0.8;
}
.main-content button:active {
opacity: 0.6;
}
我為HTML添加了一個h1標題以使其看起來更好,請確保通過向CSS添加鏈接來更新HTML:
《!DOCTYPE html》
《html lang=“en” dir=“ltr”》
《head》
《meta charset=“utf-8”》
《link rel=“stylesheet” href=“index.css”》
《title》Dice ethereum game《/title》
《/head》
《body》
《div class=“main-content”》
《h1》Ethereum Dice《/h1》
《button》Start new game《/button》
《button》Join existing game《/button》
《/div》
《/body》
《/html》
我決定顯示用戶所需的下一個操作的最佳方法是在javascript中顯示包含所需信息的div。所以當他點擊“開始新游戲”時,他會看到一個盒子,詢問他想要為游戲設置多少托管。
他點擊“加入現有游戲”,他將被要求提供現有游戲的托管和合同地址。
以下是按鈕操作的響應方式:
為了實現這一點,我用一些JavaScript邏輯創建了一個index.js文件。
function start() {
document.querySelector(‘#new-game’).addEventListener(‘click’, () =》 {
const classNewGameBox = document.querySelector(‘.new-game-setup’).className
// Toggle hidden box to display it or hide it
if(classNewGameBox === ‘new-game-setup’) {
// To hide the box
document.querySelector(‘.new-game-setup’).className = ‘hidden new-game-setup’
document.querySelector(‘#button-continue’).className = ‘hidden’
document.querySelector(‘#join-game’).disabled = false
} else {
// To show the box
document.querySelector(‘.new-game-setup’).className = ‘new-game-setup’
document.querySelector(‘#button-continue’).className = ‘’
document.querySelector(‘#join-game’).disabled = true
}
})
document.querySelector(‘#join-game’).addEventListener(‘click’, () =》 {
const classJoinGameBox = document.querySelector(‘.join-game-setup’).className
// Toggle hidden box to display it or hide it
if(classJoinGameBox === ‘join-game-setup’) {
document.querySelector(‘.new-game-setup’).className = ‘hidden new-game-setup’
document.querySelector(‘.join-game-setup’).className = ‘hidden join-game-setup’
document.querySelector(‘#button-continue’).className = ‘hidden’
document.querySelector(‘#new-game’).disabled = false
} else {
document.querySelector(‘.new-game-setup’).className = ‘new-game-setup’
document.querySelector(‘.join-game-setup’).className = ‘join-game-setup’
document.querySelector(‘#button-continue’).className = ‘’
document.querySelector(‘#new-game’).disabled = true
}
})
}
start()
解釋一下我做了什么:
1. 首先,我創建了一個名為start()的函數,該函數將會立即執行并打包內容,以便它包含在一個大函數中。
2. 然后我創建了2個事件監聽器,每當我單擊html文件中的啟動或連接按鈕時,它們就會被激活。一個用于#new-game按鈕,另一個用于#joall-game按鈕。使用document.querySelector(),這是在js代碼中選擇任何內容的最有效方法之一。
3. 在這些監聽器中,我顯示或隱藏每個相應元素的div框。基本上選擇帶有querySelector的盒子并刪除或添加隱藏的類,它在css中設置為display:none;。
然后我們可以將js文件與我們的modifie index.html連接:
《!DOCTYPE html》
《html lang=“en” dir=“ltr”》
《head》
《meta charset=“utf-8”》
《link rel=“stylesheet” href=“index.css”》
《title》Dice ethereum game《/title》
《/head》
《body》
《div class=“main-content”》
《h1》Ethereum Dice《/h1》
《button id=“new-game”》Start new game《/button》
《button id=“join-game”》Join existing game《/button》
《div class=“hidden new-game-setup”》
《h3》How much escrow will you use in ETH?《/h3》
《input type=“number” placeholder=“2.。.”》
《/div》
《div class=“hidden join-game-setup”》
《h3》What‘s the smart contract address of the existing game?《/h3》
《input type=“text” placeholder=“0x38dfj39.。.”》
《/div》
《button id=“button-continue” class=“hidden”》Continue《/button》
《/div》
《script src=“index.js”》《/script》
《/body》
《/html》
我把添加的新代碼塊加粗。以下是更新后的CSS,用于設置新信息的樣式:
body {
font-family: sans-serif;
}
.hidden {
display: none;
}
.main-content {
margin: auto;
max-width: 500px;
background-color: whitesmoke;
padding: 50px;
border-radius: 10px;
display: grid;
grid-template-rows: 1fr 80px auto;
grid-template-columns: 1fr 1fr;
grid-column-gap: 10px;
}
.main-content h1 {
grid-column: 1 / span 2;
}
.main-content button {
border: none;
color: white;
background-color: #007dff;
padding: 20px;
border-radius: 5px;
cursor: pointer;
}
.main-content button:hover {
opacity: 0.8;
}
.main-content button:active {
opacity: 0.6;
}
.main-content button:disabled {
opacity: 0.5;
background-color: grey;
cursor: auto;
}
.main-content input {
width: 100%;
border-radius: 10px;
padding: 10px;
border: 1px solid lightgrey;
}
.main-content div.new-game-setup, .main-content div.join-game-setup {
grid-column: 1 / span 2;
}
#button-continue {
grid-column: 1 / span 2;
margin-top: 20px;
}
“Continue”按鈕現在不起任何作用,所以讓我們創建一個功能來部署新的智能合約,并在用戶希望在下一節中創建新游戲時打開狀態通道。
2.創建并連接初始智能合約
現在是創建智能合約的并使用web3.js將其與JavaScript連接的時候了。現在我們只需要構造函數和一些基本信息,并將這段代碼自己寫在一個名為Dice.sol的新文件中:
pragma solidity 0.4.25;
contract Dice {
address public player1;
address public player2;
uint256 public player1Escrow;
uint256 public player2Escrow;
constructor() public payable {
require(msg.value 》 0);
player1 = msg.sender;
player1Escrow = msg.value;
}
function setupPlayer2() public payable {
require(msg.value 》 0);
player2 = msg.sender;
player2Escrow = msg.value;
}
}
有2個函數,構造函數用于設置第一個播放器的地址和托管,setupPlayer2()函數用于設置第二個播放器的信息。
我們希望在用戶單擊“continue”按鈕時部署智能合約并使用指定的msg.value執行構造函數。為此,我們必須在智能合約中實施web3.js。因為它是與瀏覽器上的區塊鏈進行通信的主要方式。
請點擊連接進行下載,單擊raw查看完整代碼并復制代碼以將其粘貼到項目文件夾中名為web3.js的新文件中:https://github.com/ethereum/web3.js/blob/develop/dist/web3.js
如果您使用metamask,則不必執行此操作,因為metamask為您注入了web3.js的版本,但如果metamask不可用,則需要項目中的web3庫與區塊鏈進行交互。
我們使用metamask與區塊鏈交互。但是當您在瀏覽器上打開index.html文件時,會打不開文件,因為metamask不支持file://擴展名。
我們需要運行一個本地服務器,將文件提交給http:// localhost:8080 url,因為當您直接打開index.html文件時,metamask不起作用。為此,請打開終端并安裝:
npm i -g http-server
然后,在項目文件夾中執行http-server以啟動index.html的本地服務器:
http-server
這將為localhost:8080上的文件提供服務,這樣您就可以訪問它們并從metamask注入Web3。
在這種情況下,讓我們集中精力部署我們剛從我們的Web應用程序創建的合同,就在用戶單擊“continue”時。
要部署新合同,我們需要ABI,構造函數參數和字節碼。這些是web3.js的要求。
要生成ABI,請轉到remix.ethereum.org,將代碼粘貼到主部分,然后單擊ABI:
這將復制ABI代碼。轉到項目文件夾并創建一個名為contractData.js的文件,將代碼粘貼到一個名為abi的變量,如下所示:
const abi = [
{
“constant”: true,
“inputs”: [],
“name”: “player2Escrow”,
“outputs”: [
{
“name”: “”,
“type”: “uint256”
}
],
“payable”: false,
“stateMutability”: “view”,
“type”: “function”
},
{
“constant”: true,
“inputs”: [],
“name”: “player1Escrow”,
“outputs”: [
{
“name”: “”,
“type”: “uint256”
}
],
“payable”: false,
“stateMutability”: “view”,
“type”: “function”
},
{
“constant”: true,
“inputs”: [],
“name”: “player2”,
“outputs”: [
{
“name”: “”,
“type”: “address”
}
],
“payable”: false,
“stateMutability”: “view”,
“type”: “function”
},
{
“constant”: false,
“inputs”: [],
“name”: “setupPlayer2”,
“outputs”: [],
“payable”: true,
“stateMutability”: “payable”,
“type”: “function”
},
{
“constant”: true,
“inputs”: [],
“name”: “player1”,
“outputs”: [
{
“name”: “”,
“type”: “address”
}
],
“payable”: false,
“stateMutability”: “view”,
“type”: “function”
},
{
“inputs”: [],
“payable”: true,
“stateMutability”: “payable”,
“type”: “constructor”
}
]
2.現在我們需要智能合約的bytecode。bytecode是將被部署到區塊鏈的已編譯的智能合約,我們需要該信息才能部署它。要使bytecode再次重新混合并單擊此按鈕:
并在contractData.js中創建另一個變量,稱為betycode,其信息如下:
const abi = [
{
“constant”: true,
“inputs”: [],
“name”: “player2Escrow”,
“outputs”: [
{
“name”: “”,
“type”: “uint256”
}
],
“payable”: false,
“stateMutability”: “view”,
“type”: “function”
},
{
“constant”: true,
“inputs”: [],
“name”: “player1Escrow”,
“outputs”: [
{
“name”: “”,
評論
查看更多