libra-auth
Version:
An authentication method using Libra's Public Key and Secret Key.
459 lines (417 loc) • 15.7 kB
HTML
<html>
<head>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1">
<title>Ticket Center</title>
<link rel="stylesheet" href="./css/main.css">
<style>
</style>
<script src="./js/libraweb-v2.js"></script>
<script
src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha256-pasqAKBDmFT4eHoN2ndd6lN370kFiGUFyTiUHWhU7k8="
crossorigin="anonymous"></script>
</head>
<body>
<div id=contents>
<section>
<img class=logo alt="libra logo" src="./img/libra.png">
</section>
<header>
<div class=header>
<h2>Bob's Ticket Center</h2>
The ticket price is <strong>10 Libra</strong>. If Buy Button click sent 10 Libra and you can take the Ticket.
</div>
</header>
<section>
<div id=buy-button></div>
</section>
<section>
<div id=balance></div>
</section>
<footer>
<div id=powerd>
<nav>
powerd by <a href="https://github.com/toshirot/libra-auth">libra-auth</a>
</nav>
</div>
</footer>
</div>
</body>
<script>
$(function(){
// Eventually the libraweb code will be Hide from window object later.
//=============================================================================
// main object
//
//------------------------------------------------------------------------------
//------------------------------------------------------------
// Object for Ticket Demo with Libra Auth method
//
// Thanx: libraweb loaded by libraweb-v2.js is browserified
// bandifycol/libra-web and perfectmak/libra-core.
// Alice's client is connected to proxy ac-libra-testnet.kulap.io in kulapio/libra-core.
let Ticket={
libraweb: window.libraweb//Hide from window later
,librawebBuffer: window.librawebBuffer//Hide from window later
,conn: {
wss_arry: []
,wss: null
,wsNonStopFlg:true
}//for WebSocket
};
//=============================================================================
// data
//
//------------------------------------------------------------------------------
//------------------------------------------------------------
// for LIBRA
//In this demo, the address is fixed beforehand.
const fromAddrAlice='9291e14f5e7c1ce211ce9477154faddb0c308af2f6a10324f893e2e59a13ff80'
const toAddrBob='9291e14f5e7c1ce211ce9477154faddb0c308af2f6a10324f893e2e59a13ff80'
//unit of Libra
const LIBRA_UNIT=1000000
//The ticket price is 10 Libra
const TICKET_AMOUNT=10*LIBRA_UNIT;//10*1000000 is 10 Libra
//First mint for Alice
const FIRST_MINT_FOR_ALICE=TICKET_AMOUNT*2
//host for mint
const DEFAULT_FAUCET_HOST='faucet.testnet.libra.org'
//protocol for mint. http or https
const DEFAULT_FAUCET_PROTOCOL='http'
//------------------------------------------------------------
// for DOM
//loading html
const LOADING_HTML='<div class="loader">Loading...</div>'
//buy button
const BUY_BUTTON_HTML=
`<form>
<button id=btn class="btn-buy ">Buy</button>
</form>`
//------------------------------------------------------------
// for MESSAGE
const M_DO_YOU_BUY_CONFIRM='Do you want to buy a ticket? The Cost is Libra 20.'
//------------------------------------------------------------
// for WebSocket
const HB=JSON.stringify({type:'hb'})//heartbeat
const HEARTBEAT_INTERVAL=10000//60000
//=============================================================================
// Dom operations
//
//------------------------------------------------------------------------------
//------------------------------------------------------------
// onload
//make buy button
mkHtml('#buy-button', BUY_BUTTON_HTML)
//
$('#balance').html(LOADING_HTML)
// mint 20 Libra to Allice
doMint(fromAddrAlice, FIRST_MINT_FOR_ALICE)
getBalance(fromAddrAlice, function(balance){
$('#balance').html(mkClientProfeel(fromAddrAlice, balance))
})
//------------------------------------------------------------
// Event for click or tap
const clickevt = 'ontouchstart' in window ? 'touchstart' : 'click';
$('.btn-buy').bind(clickevt, function(){
event.preventDefault()
// some codes for click or tap
$(this).css({background: '#bbb'})
//chk do you buy?
let okbuy=checkConfirm(M_DO_YOU_BUY_CONFIRM)
if(okbuy){
// buy
let res = getPubKey(12345, 0)
console.log(res)
$('#buy-button').html(LOADING_HTML)
setTimeout(function(){
$('#buy-button').html('lets go chk tx')
},1000)
} else {
// Not buy
$(this).css({background: '#39298C'})
}
})
//=============================================================================
// WebSocket Operations
//
//-----------------------------------------------------------------------------
let wss=wssLibraAuth('wss://wss.libra-auth.com:8888/')
window.wss=wss//
//------------------------------------------------------------
// pre) Connect with WebSocket
// @url {String} target wss Server
// @return {Object} WebSocket instance
function wssLibraAuth(url){
let op={
protocol: 'wss.libra-auth.com'
,uid: 'wss-' + uuidv4()
,hbTimeout:null
}
let wss = new WebSocket(url, op.protocol)
console.log(Ticket.conn, Ticket.conn.wss_arry)
Ticket.conn.wss_arry.push(wss)
wss.op=op
wss.on = function (type, fnc){
this.addEventListener(type, fnc)
return this
}
wss
.on('open', function(){heartbeat(wss)})
.on('message', function(msg){onmsg(wss,msg)})
.on('close', function clear() {
clearTimeout(this.pingTimeout)
console.log('closed wss:',this.op.uid)
})
return wss;
}
//------------------------------------------------------------
// heartbeat
function heartbeat(wss) {
// Start heartbeat send
clearInterval(wss.op.hbTimeout);
wss.send(HB);
wss.op.hbTimeout = setInterval(() => {
if(wss.readyState===WebSocket.OPEN){
wss.send(HB);
} else {
wssDelAll()
}
}, HEARTBEAT_INTERVAL);
}
//------------------------------------------------------------
// pre) onmsg
// @wss {Object} WebSocket instance
// @msg {String} stringifyd json message
function onmsg(wss, msg){
if(!wss)return
if(!msg)return
if(!msg.data)return
let data
try{
data = JSON.parse(msg);
if(msg.data.type!==undefined){
console.log(1, data)
return
} else if(msg.data.type==='hb'){
data=msg.data
console.log(2, data)
} else if(msg.data.type==='sigs'){
data=msg.data
console.log(3, data)
} else {
console.log(4, data)
}
} catch(e){ return }
if(!data.sigs)return
alert('received',data.sigs)
}
window.wssDelAll=wssDelAll
function wssDelAll(callback) {
let wss_arry=Ticket.conn.wss_arry
//del all wss object
for(var i=0;i<wss_arry.length;i++){
console.log(i
, 'deleted wss'
, (wss_arry[i].op.uid||'')
,new Date()
)
wss_arry[i].close()
}
Ticket.conn.wss_arry=[]
if(callback)callback()
}
//=============================================================================
// Util Functions
//
//-----------------------------------------------------------------------------
//------------------------------------------------------------
// pre) mk html
// @targetElm {String} selector for jquery e.g. '#hoge'
// @html {String} html
function mkHtml(targetElm, html){
$(targetElm).html(html)
}
//------------------------------------------------------------
// pre) clientプロフィールのHTMLを作る
// @return {String} html
function mkClientProfeel(fromAddrAlice, balance){
return `
<div class=youraddr><span>Your Address:</span>${fromAddrAlice}</div>
<div class=yourbalance><span>Your Balance:</span>${balance}</div>`
}
//------------------------------------------------------------
// pre) clientを作る
// @return {String} addres hex
function createClient(){
let address=''
return address
}
//------------------------------------------------------------
// pre) onload時にここでは事前にmintで入金する
// @address {Number} libra address
// @amount {Number} libra amount
function doMint(address, amount, host, protocol){
if(!chkInput())return
if(!host)host=DEFAULT_FAUCET_HOST
if(!protocol)protocol=DEFAULT_FAUCET_PROTOCOL
let url=`http://${host}?amount=${amount}&address=${address}`
fetch(url, {
method: 'post',
mode: 'no-cors'
}).then(data =>
console.log(data)
).catch(error => console.error(error))
function chkInput(){
//simple check input
if(!address){
alert('address is required.')
return false
}
try{amount=+amount}catch(e){
alert('amount must be Number')
return false
}
//if(amount>TICKET_AMOUNT){ alert('amount is over 10 Libra. \n amount:'+amount+'/ '+TICKET_AMOUNT); return false }
return true
}
}
//------------------------------------------------------------
// pre) get balance of address
// @address {string} target address
// @callback{function} callback
function getBalance(address, callback){
const client = new Ticket.libraweb.LibraClient({
protocol: 'https',
host: 'ac-libra-testnet.kulap.io',
port: '443',
// dataProtocol: 'grpc-web-text'
})
const addr = new Ticket.libraweb.AccountAddress(
Uint8Array.from(
Ticket.librawebBuffer.from(address, 'hex')
)
)
const accountState =
client
.getAccountState(addr)
.then((value) => {
if(callback)callback(value.balance)
}, (reason) => {
console.log('error:',reason)
})
}
//------------------------------------------------------------
// 1) Buy buttonを押したときに送金の意志を確認する
// @msg {String} massage
// @retuen {Bool} true|false
function checkConfirm(msg){
return window.confirm(msg)
}
//------------------------------------------------------------
// 2) from address から to addressへ送金する
// @from {String} libra from address
// @to {String} libra to address
// @amount {Number} libra amount
function transfer(from, to, amount){
}
//------------------------------------------------------------
// 3)4) 最新のシークエンス番号を取得する
// @addrees {Number} libra addrees
// @return sequence{Number} last sequence number
function getSequence(addrees){
let sequence
return sequence
}
//------------------------------------------------------------
// 3)4) トランザクションを取得する
// @addrees {Number} libra addrees
// @sequence{Number} sequence number
// @return {object} transaction
function getTx(addrees, sequence){
//get tx from testnet
let tx={
public_key: 'test'
}
return tx
}
//------------------------------------------------------------
// 3)4) pubKeyを取得する
// @addrees {Number} libra addrees
// @sequence{Number} sequence number
// @return {String} pubKey
function getPubKey(addrees, sequence){
let tx = getTx(addrees, sequence)
let pubKey=tx.public_key
return pubKey
}
//------------------------------------------------------------
// 5) メッセージのハッシュを取得する
// @msg {String} message
// @return msg hash{String} msg hash by SHA3
function mkMsgHash(msg){
return (new SHA3(512)).update(msg).digest('hex')
}
//------------------------------------------------------------
// 5)7) メッセージと秘密鍵絵でシグネチャを生成する
// @msg {String} message
// @prikey {String} Private key
// @return msg hash{String} msg hash by SHA3
function mkMsgHash(msg, prikey){
return prikey.sign(msg).toHex()
}
//------------------------------------------------------------
// 5) WebSocketでsignatureとmassageを送る
// @msg {String} message
// @sig {String} signature
function sendMsgAndSig(msg, sig){
const oj=JSON.stringify({
msg: msg,
sig, sig
})
if(window.wss){
window.wss.send(oj)
}
}
//------------------------------------------------------------
// 6)8) メッセージとシグネチャを公開鍵で検証する
// @msg {String} message
// @sig {String} signature
// @pubkey {String} Public key
// @return {bool} true|false
function mkMsgHash(msg, sig, pubkey){
//
return pubkey.verify(msg, sig)
}
//------------------------------------------------------------
// 7) QRコードを生成する
// @sig {String} signature
// @return qr {Image} QR png image for of sig
function mkMsgHash(sig){
return //qr
}
// -----------------------------------------------------------------------------
// uuidv4
//
function uuidv4() {
// Thanx for
// https://gist.github.com/jcxplorer/823878
// https://web.archive.org/web/20150201084235/http://blog.snowfinch.net/post/3254029029/uuid-v4-js
let uuid = ''
let random;
for (let i = 0; i < 32; i++) {
random = Math.random() * 16 | 0;
if (i == 8 || i == 12 || i == 16 || i == 20) {
uuid += '-';
}
uuid += (i == 12 ? 4 : (i == 16 ? (random & 3 | 8) : random)).toString(16);
}
setTimeout(function() {
uuid=random=null;
}, 1000);
return uuid;
}
})
</script>
</html>