有很多不同的解决方案可以创建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”: “”,
评论
查看更多