Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions Bank
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kert-One | Digital Bank</title>

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"></script>
<script src="https://unpkg.com/html5-qrcode"></script>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/elliptic/6.5.4/elliptic.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
</head>

<body class="bg-gray-100 flex justify-center min-h-screen">
<div class="w-full max-w-md bg-white min-h-screen shadow-xl flex flex-col">

<header id="appHeader" class="hidden p-4 flex justify-between items-center bg-white shadow">
<b id="displayUsername">Usuário</b>
<button onclick="logout()">Sair</button>
</header>

<main class="flex-1 p-4">

<div id="auth">
<button onclick="generateWallet()" class="bg-orange-500 text-white w-full p-3 rounded mb-2">Criar Conta</button>
<textarea id="pk" class="border p-2 w-full mb-2" placeholder="Private Key"></textarea>
<button onclick="importWallet()" class="bg-black text-white w-full p-3 rounded">Importar</button>
</div>

<div id="main" class="hidden">
<div class="bg-black text-white p-4 rounded mb-4">Saldo: <span id="balance">0.00</span> K$</div>
<div class="text-xs break-all mb-4">Endereço: <span id="addr"></span></div>

<input id="to" placeholder="Destino" class="border p-2 w-full mb-2">
<input id="amount" type="number" placeholder="Valor" class="border p-2 w-full mb-2">
<button onclick="processPayment()" class="bg-orange-500 text-white w-full p-3 rounded mb-4">Enviar</button>

<h3 class="font-bold">Histórico</h3>
<div id="history"></div>
</div>

</main>
</div>

<script>
const API = window.location.origin;
const EC = new elliptic.ec('secp256k1');
let wallet = {};

/* LOAD */
window.onload = () => {
const w = localStorage.getItem('wallet');
if(w){ wallet = JSON.parse(w); loginSuccess(false); }
};

/* WALLET */
function generateWallet(){
const key = EC.genKeyPair();
wallet = {
private: key.getPrivate('hex'),
public: key.getPublic('hex'),
address: 'k1'+CryptoJS.SHA256(key.getPublic('hex')).toString().substring(0,38)
};
localStorage.setItem('wallet', JSON.stringify(wallet));
loginSuccess();
}

function importWallet(){
const pk = document.getElementById('pk').value.trim();
const key = EC.keyFromPrivate(pk,'hex');
wallet = {
private: pk,
public: key.getPublic('hex'),
address: 'k1'+CryptoJS.SHA256(key.getPublic('hex')).toString().substring(0,38)
};
localStorage.setItem('wallet', JSON.stringify(wallet));
loginSuccess();
}

function loginSuccess(){
document.getElementById('auth').classList.add('hidden');
document.getElementById('main').classList.remove('hidden');
document.getElementById('appHeader').classList.remove('hidden');
addr.innerText = wallet.address;
updateBalance();
loadHistory();
setInterval(loadHistory, 8000);
}

/* BALANCE */
async function updateBalance(){
const r = await axios.get(`${API}/balance/${wallet.address.replace('k1','')}`);
balance.innerText = parseFloat(r.data.balance).toFixed(2);
}

/* SIGN TX */
function signTx(recipient, amount) {
const txId = crypto.randomUUID();
const fee = 0.01;
const timestamp = Date.now();

const tx = {
id: txId,
sender: wallet.address,
recipient: recipient,
amount: parseFloat(amount).toFixed(8),
fee: fee.toFixed(8),
timestamp: timestamp
};

// Ordenar igual Python sort_keys=True
const ordered = {};
Object.keys(tx).sort().forEach(k => ordered[k] = tx[k]);

const hash = CryptoJS.SHA256(JSON.stringify(ordered)).toString();

const key = EC.keyFromPrivate(wallet.private, 'hex');
const signature = key.sign(hash).toDER('hex');

return {
...ordered,
public_key: wallet.public,
signature: signature
};
}


/* SEND */
async function processPayment(){
const to = document.getElementById('to').value.trim();
const amount = parseFloat(document.getElementById('amount').value);
const tx = signTx(to, amount);
await axios.post(`${API}/tx/new`, tx);
updateBalance();
loadHistory();
}

/* HISTORY REAL */
async function loadHistory(){
const r = await axios.get(`${API}/chain`);
const chain = r.data.chain;
const myAddr = wallet.address.replace("k1","");
history.innerHTML="";

chain.forEach(block=>{
block.transactions.forEach(tx=>{
if(tx.sender===myAddr || tx.recipient===myAddr){
const conf = chain.length - block.index + 1;
history.innerHTML += `
<div class="border p-2 mb-2 text-xs rounded">
${tx.sender===myAddr?'Enviado':'Recebido'} K$ ${tx.amount}<br>
Confirmações: ${conf}<br>
TX: ${tx.id}
</div>`;
}
});
});
}

function logout(){ localStorage.clear(); location.reload(); }
</script>
</body>
</html>