5

鲜衣怒马散尽千金,Vue3.0+Tornado6前后端分离集成Web3.0之Metamask钱包区块链虚拟货...

 2 years ago
source link: https://v3u.cn/a_id_219
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

鲜衣怒马散尽千金,Vue3.0+Tornado6前后端分离集成Web3.0之Metamask钱包区块链虚拟货币三方支付功能

首页 - Python /2022-07-27
鲜衣怒马散尽千金,Vue3.0+Tornado6前后端分离集成Web3.0之Metamask钱包区块链虚拟货币三方支付功能

    不得不承认,大多数人并不拥有或者曾经拥有加密货币。是的,Web3.0、加密货币、区块链,对于大多数的互联网用户来说,其实是一些过于轻佻的词汇。如果你是为了追求暴利投机而研究区块链和加密货币,那你多半会失望,因为盐在哪里都是咸的;而如果你是为了摆脱知识桎梏而学习区块链,那你几乎一定能满足,因为懵懂决不是编程界的常态。

    对于支付系统来说,加密货币的主要好处之一是去中心化,这意味着它由许多团队或团体控制和管理,而不是一个单一的中心化机构,暗箱操作在这里并不存在,这为系统带来了透明度。加密货币的另一个好处是它是一个点对点系统,因为它可以发送给世界上任何人,消除了微信、支付宝等第三方支付寡头的干预,这使得它们具有成本效益。

    本次,就让我们来为支付系统添上加密货币支付的这一笔,通过Vue3.0+Tornado6的前后端分离系统,一睹区块链加持下去中心化支付逻辑的风采。

    首先,我们当然需要一个加密货币钱包,关于系统集成MetaMask钱包的逻辑,请参见之前的一篇:青山不遮,毕竟东流,集成Web3.0身份钱包MetaMask以太坊一键登录(Tornado6+Vue.js3)

    其后,我们需要为接下来的支付行为领取一些“测试加密货币”,领取测试币的网站被称为水龙头(faucet),测试币被称为水,所以领取测试币的过程也被叫做领水。

    我们以 Rinkeby 的测试币领取为例讲解过程,其他测试网的测试币领取方式类似,如果愿意,大家可以把几个测试网的水都领一些。

    第一步,打开钱包插件,选择一个钱包,点击设置:

20220727210717_23114.png

    随后,选择高级,然后启用测试网络:

20220727210708_32654.png

    接着,将网络切换到Rinkeby测试网络,网络中还可以看到 Ropsten、Kovan和 Goerli等其他三个测试网络,四个测试网络各有特点, Ropsten 采用 POW 机制,可以自己搭建节点挖测试币,但是稳定性较差,偶尔还会遇到区块回滚的情况,很多实验性测试,比如 “区块阻塞攻击” 实验会放到这个测试网来测试; Kovan 、Rinkeby 和 Goerli 是采用 POA 机制,这几个测试网络不能通过挖矿的方式获取测试币,只能通过水龙头领取,我们以 Rinkeby 为例讲解领取过程, Kovan 和 Goerli 类似,但领取条件更为严格,大家可以根据需要领取。

    切换好Rinkeby测试网络后,访问水龙头网站:https://faucets.chain.link/rinkeby  通过钱包进行链接登录,然后将钱包地址填入领取表单,即可领取0.1的eth货币:

20220727210751_84785.png

    领取交易确认之后,查看钱包余额:

20220727210753_65942.png

    至此,前期准备工作就完成了。

    钱包支付加签

    前端在Vue3.0项目中安装区块链模块和异步请求模块:

npm install --save ethers

npm install --save axios

    随后在组件中导入区块链模块:

import {ethers} from 'ethers';

    并且对axios进行简单封装:

const myaxios = function(url,type,data={}){

return new

Promise((resolve) => {


//判断
if(type==="get" || type === "delete"){


axios({

method:type,
url:url,
params:data
}).then((result) => {

resolve(result.data);

});


}else{


axios({

method:type,
url:url,
data:qs.stringify(data)
}).then((result) => {

resolve(result.data);

});

}


});

}

const app = createApp(App)
app.config.globalProperties.myaxios = myaxios;

    接着,当页面首次加载时,我们希望检查用户是否已经将钱包连接到站点。为此,我们需要使用eth_accounts方法获取用户的帐户。如果没有返回帐户,这意味着用户没有连接:

checkIfWalletConnected:function() {


window.ethereum.request({ method: 'eth_accounts' }).then(function (accounts) {
if (accounts.length > 0) {


console.log(accounts[0]);

} else {

alert("应用未链接钱包");
}

});
}

    随后,在初始化方法内对当前用户进行检测:

created(){


this.checkIfWalletConnected();


}

    如果用户钱包链接没问题,那么构建支付表单:

<input type="text" v-model="amount" />

<a-button type="primary" @click="create_sign">点击支付</a-button>

    这里用户点击支付按钮后,进入加签逻辑:

create_sign:function(){


var provider = new ethers.providers.Web3Provider(web3.currentProvider);

//获取签名对象
var signer = provider.getSigner();

//时间戳
var rightnow = (Date.now()/1000).toFixed(0);

var sortanow = rightnow - (rightnow % 600);

//生成签名
signer.signMessage("Trade with "+document.domain+" at "+sortanow+" for "+this.amount,this.accountaddress,"test").then((signature) => {


this.check_sign(signature);


});

}

    这里通过Web3Provider获取到签名实例,随后根据时间戳+域名+支付金额生成签名,签名生成后,立刻调用check_sign方法向后台发起异步请求进行验签操作:

check_sign:function(signature){

this.myaxios(this.weburl+"/sign/","post",{"signature":signature,"accountaddress":this.accountaddress,"amount":this.amount}).then(data =>{

if(data.errcode == 0){

this.makePaymentRequest(data.data.selleraddress,data.data.amount);

}


});


}

    这里将签名和钱包地址发送给后端,在客户端与钱包请求交互的过程中,请求的数据很容易被拦截并篡改,所以加签环节必不可少:

20220727210736_20243.png

    后端验签并创建交易

    后端需要web3模块的加持:

pip3 install web3

    随后创建验签方法:

from web3.auto import w3
# 反编译方法
from eth_account.messages import defunct_hash_message
import time

class CheckSign(BaseHandler):


async def post(self):

signature = self.get_argument("signature",None)

accountaddress = self.get_argument("accountaddress",None)

amount = self.get_argument("amount",None)

selleraddress = "0x95f57Bf3837325FE99a611EFacff6b1d70C7731A"


# 获取当前域名
domain = self.request.host

if ":" in domain:

domain = domain[0:domain.index(":")]

# 时间戳
now = int(time.time())

sortanow = now - now % 600

# 生成签名
message = "Trade with {} at {} for {}".format(domain,sortanow,amount)

print(message)

# 反编译
message_hash = defunct_hash_message(text=message)

# 获取签名对象
signer = w3.eth.account.recoverHash(message_hash,signature=signature)

print(accountaddress,signer)

if accountaddress == signer.lower():

res = {"errcode":0,"msg":"ok","data":{"selleraddress":selleraddress,"amount":str(w3.toWei(amount,'ether'))}}

else:

res = {"errcode":1,"msg":"failed"}

self.finish(res)

    这里后端通过同样的算法对签名进行验证,如果验签通过,后端将会返回商户的钱包地址,也就是用户转账的钱包地址,同时会将付款金额通过w3.toWei方法进行转换,以太币的最小单位为wei,1个以太币相当于10的8次方wei。通常,大家也使用Gwei作为展示单位。比较常用的就是eth,Gwei和wei。

    但为了统一标准,支付表单汇总显示的是eth最大单位,所以通过toWei方法,将最大单位转换为最小单位,即0.001eth=100000000000000wei,注意转换后需要以字符串的形式返回到前端。

    随后后端将商户钱包地址和转换后的支付金额返回给前端。

    回到前端,验签通过后,前端获取支付钱包地址和金额,旋即通过钱包创建支付:

makePaymentRequest:function(sellerAddress,amount) {


window.ethereum.request({ method:'eth_sendTransaction', params: [{ from:this.accountaddress, to:sellerAddress,value:amount}] })
.then(response => {
console.log("交易号:"+response);
var orderid = response;
})
.catch(error => {
console.log(error);
});

}

    通过eth_sendTransaction方法发起交易,当用户同意支付请求时,将会返回此笔交易的TransactionHash,也就是交易哈希号:

20220727220745_96264.png

    确认支付交易后,获取TransactionHash:

交易号:0xe937c66e337322cf3b83788b495af2da35ff9635aaaa20f156c74c7f7fddad26

    事实上,每一笔支付交易都会产生另一笔“燃料费”,交易燃料费将归属于挖出区跨链中本次交易区块的矿工。当矿工挖矿时,他需要决定哪些交易放入到区块中,可以随机选择交易, 也可以不包含任何交易。为了鼓励让矿工将你的交易放入区块,相应地,你必须付出一部分“小费”。

    支付确认之后,我们可以利用Rinkeby网络站点通过输入交易哈希号来查询这一笔交易:https://rinkeby.etherscan.io/tx/0xe937c66e337322cf3b83788b495af2da35ff9635aaaa20f156c74c7f7fddad26:

20220727220754_20260.png

    当然了,让用户自己在“水龙头”上查询支付结果显然不怎么讲究,后端肯定需要记录交易哈希号并且查询交易明细,这里我们需要一个“上链”服务,让我们的后端也接上区块链网络,访问 https://infura.io/

    Infura是一种IaaS产品,目的是为了降低访问以太坊数据的门槛。 对于开发者来说,Infura是一个可以让你的dApp快速接入以太坊的平台,不需要本地运行以太坊节点。 Infura背后是负载均衡的API节点集群。 有针对以太坊Infura有一系列的开发套件。

    注册后,创建链接项目:

20220727220740_15386.png

    随后,复制Rinkeby节点链接:

20220727220703_68814.png

    接着,创建订单查询脚本 checkorder.py:

from web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://rinkeby.infura.io/v3/32ff12c27a9c485db9a7b61b0a7f3f61"))

print(w3.isConnected())

print(w3.eth.getTransactionReceipt("0xe937c66e337322cf3b83788b495af2da35ff9635aaaa20f156c74c7f7fddad26"))

    这里一旦“上链”成功,就可以根据交易哈希号来查询交易的明细,系统返回:

True
AttributeDict({'blockHash': HexBytes('0x4ede42c4bd15c7ce1736523ae1f84284c7bbdc17388cfbae0df2897bf19f287c'), 'blockNumber': 11098045, 'contractAddress': None, 'cumulativeGasUsed': 13409523, 'effectiveGasPrice': 1500000017, 'from': '0x3B14DdBa7FFF887ED3CCF01fCa0b84501Fd7a711', 'gasUsed': 21000, 'logs': [], 'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), 'status': 1, 'to': '0x95f57Bf3837325FE99a611EFacff6b1d70C7731A', 'transactionHash': HexBytes('0xe937c66e337322cf3b83788b495af2da35ff9635aaaa20f156c74c7f7fddad26'), 'transactionIndex': 16, 'type': '0x2'})

    至此,完成的加密货币支付逻辑就完成了,大体流程如下:

    1.  应用会载入并自动检查 Metamask 钱包是否已连接。如果没有,将会提示用户安装钱包插件并且链接。
    2. 交易加签操作。

    3. 后端验签,并且返回商户钱包地址以及转换金额。

    4. 钱包创建交易。

    5. 用户审核并确认付款。
    6. 用户确认交易,生成交易号,用户和应用都会收到付款确认。

    很遗憾,用户在向钱包地址发送加密货币时必须非常小心,如果有人将加密货币发送到任何错误的地址,用户将无法取消交易或提出任何投诉以获得退款,是的,deal is deal,当交易行为已经被写入区块,那么是无法被撤销的。

    但这并不意味的用户就会因此和平台商户产生纠纷,如果沟通之后,达成了退款协议,加密货币也可以直接从后台进行转账操作:

from web3 import Web3
import os

w3 = Web3(Web3.HTTPProvider("https://rinkeby.infura.io/v3/32ff12c27a9c485db9a7b61b0a7f3f61"))

print(w3.isConnected())

address1 = w3.toChecksumAddress('selleraddress')
address2 = w3.toChecksumAddress ('accountaddress')
private_key = os.getenv('PRIVATE_KEY')
# in this case, the nonce is the amount of transactions on accounti
nonce = w3.eth.getTransactionCount(address1)
# setup the transaction
tx ={
'nonce':nonce,
"to":address2,
'value':w3.toWei("0.0001","ether"),
"gas": 21000,
"gasPrice":w3.toWei(40,'gwei'),
}
signed_tx = w3.eth.account.signTransaction(tx, private_key)

tx_hash = w3.sendRawTransaction(signed_tx.rawTransaction)

    这里商户只需要将钱包私钥导入环境变量,随后创建交易并通过私钥加签,最后确认交易,并且获取到交易哈希号。

    毫无疑问,加密货币会损害一部分传统行业既得利益者的利益,但也不能不承认,加密货币更是二十一世纪的一记响雷,就像洪荒时代孑余的一头恐龙、大戈壁中枝叶扶疏的一株胡杨、兵马俑阵中一个脉搏跳动体温犹存的肉身、死寂的山谷中凭空乍响的一声洪钟。所谓衣不如新,人不如故,不入春园,怎知春色几许?所谓技术的本质就是最大额度地收获创新,你同意吗?


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK