Upload files to "src/main/resources/static"
This commit is contained in:
338
src/main/resources/static/client3.html
Normal file
338
src/main/resources/static/client3.html
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Secure Messaging Client</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
||||||
|
h2 { border-bottom: 2px solid #ccc; padding-bottom: 5px; }
|
||||||
|
label { display: block; margin-top: 10px; font-weight: bold; }
|
||||||
|
input[type="text"], textarea, select { width: 100%; padding: 8px; margin-top: 5px; box-sizing: border-box; }
|
||||||
|
button { padding: 10px 15px; margin-top: 10px; cursor: pointer; background-color: #007bff; color: white; border: none; border-radius: 4px; }
|
||||||
|
button:hover { background-color: #0056b3; }
|
||||||
|
#keys { background-color: #f4f4f4; padding: 15px; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 20px; }
|
||||||
|
.key-pair div { margin-bottom: 15px; }
|
||||||
|
#log { margin-top: 20px; padding: 10px; background-color: #e9ecef; border: 1px solid #ced4da; height: 150px; overflow-y: scroll; white-space: pre-wrap; }
|
||||||
|
.message-box { border: 1px solid #ddd; padding: 15px; margin-bottom: 10px; border-radius: 4px; }
|
||||||
|
.encrypted-message { color: #888; font-style: italic; font-size: 0.9em; }
|
||||||
|
.decrypted-message { color: #000; font-weight: bold; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Encrypted Messaging Client</h1>
|
||||||
|
|
||||||
|
<div id="keys">
|
||||||
|
<h2>🔑 Your Identity (Alice)</h2>
|
||||||
|
<p>Generate a new RSA key pair for Alice, or paste existing keys (in Base64 X.509/PKCS#8 format).</p>
|
||||||
|
<button id="generateKeysBtn">1. Generate New Key Pair</button>
|
||||||
|
<div class="key-pair">
|
||||||
|
<label for="publicKey">Your Public Key (X.509 Base64 - for sharing):</label>
|
||||||
|
<textarea id="publicKey" rows="5" placeholder="Public Key"></textarea>
|
||||||
|
<label for="privateKey">Your Private Key (PKCS#8 Base64 - MUST BE KEPT SECRET):</label>
|
||||||
|
<textarea id="privateKey" rows="5" placeholder="Private Key"></textarea>
|
||||||
|
</div>
|
||||||
|
<p>Use a single line for the keys to match the server's clean processing of Base64 strings.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div id="send">
|
||||||
|
<h2>✉️ Send Message</h2>
|
||||||
|
<label for="recipientKey">Recipient's Public Key (The key you encrypt FOR):</label>
|
||||||
|
<textarea id="recipientKey" rows="3" placeholder="Paste Recipient's Public Key here (e.g., Bob's Key)"></textarea>
|
||||||
|
|
||||||
|
<label for="messageContent">Message Content (Plaintext):</label>
|
||||||
|
<textarea id="messageContent" rows="3" placeholder="Type your secret message here..."></textarea>
|
||||||
|
|
||||||
|
<button onclick="sendMessage()">2. Encrypt, Sign, & Send Message</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div id="receive">
|
||||||
|
<h2>📥 Retrieve Messages</h2>
|
||||||
|
<p>The server logic requires a signature even for a retrieve request.</p>
|
||||||
|
<button onclick="retrieveMessages()">3. Retrieve & Decrypt Messages</button>
|
||||||
|
<div id="incomingMessages">
|
||||||
|
<p>Decrypted messages will appear here.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h2>Console Log</h2>
|
||||||
|
<div id="log">Awaiting action...</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// --- CONFIGURATION ---
|
||||||
|
const SERVER_URL = "http://localhost:8080/messages"; // **CHANGE THIS TO YOUR SERVER URL**
|
||||||
|
|
||||||
|
// RSA Algorithm Constants for Web Crypto API
|
||||||
|
const SIGN_ALGORITHM = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
|
||||||
|
const ENCRYPT_ALGORITHM = { name: "RSA-OAEP", hash: "SHA-256" };
|
||||||
|
const KEY_PARAMS = {
|
||||||
|
name: "RSA-OAEP",
|
||||||
|
modulusLength: 2048,
|
||||||
|
publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537
|
||||||
|
hash: "SHA-256"
|
||||||
|
};
|
||||||
|
const KEY_USAGES = ["encrypt", "decrypt"];
|
||||||
|
|
||||||
|
// Imported keys stored internally as CryptoKey objects
|
||||||
|
let cryptoKeyPair = null;
|
||||||
|
|
||||||
|
// --- UTILITY FUNCTIONS ---
|
||||||
|
|
||||||
|
/** Logs a message to the console and the HTML log area. */
|
||||||
|
function log(message) {
|
||||||
|
console.log(message);
|
||||||
|
const logDiv = document.getElementById('log');
|
||||||
|
logDiv.innerHTML = new Date().toLocaleTimeString() + " | " + message + "\n" + logDiv.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts ArrayBuffer to Base64 (needed for crypto exports/imports). */
|
||||||
|
function bufferToBase64(buffer) {
|
||||||
|
return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts Base64 to ArrayBuffer (needed for crypto exports/imports). */
|
||||||
|
function base64ToBuffer(b64) {
|
||||||
|
return Uint8Array.from(atob(b64), c => c.charCodeAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts a string to ArrayBuffer for signing/encrypting. */
|
||||||
|
function strToBuffer(str) {
|
||||||
|
return new TextEncoder().encode(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts ArrayBuffer to string for decryption result. */
|
||||||
|
function bufferToStr(buffer) {
|
||||||
|
return new TextDecoder().decode(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- KEY MANAGEMENT ---
|
||||||
|
|
||||||
|
document.getElementById('generateKeysBtn').onclick = async () => {
|
||||||
|
try {
|
||||||
|
// Generate a full key pair suitable for both signing and encryption/decryption
|
||||||
|
cryptoKeyPair = await crypto.subtle.generateKey(KEY_PARAMS, true, KEY_USAGES);
|
||||||
|
|
||||||
|
// Export Public Key (SPKI format, required by Java's X509EncodedKeySpec)
|
||||||
|
const pubBuffer = await crypto.subtle.exportKey('spki', cryptoKeyPair.publicKey);
|
||||||
|
document.getElementById('publicKey').value = bufferToBase64(pubBuffer);
|
||||||
|
|
||||||
|
// Export Private Key (PKCS8 format, required for server-side compatibility)
|
||||||
|
const privBuffer = await crypto.subtle.exportKey('pkcs8', cryptoKeyPair.privateKey);
|
||||||
|
document.getElementById('privateKey').value = bufferToBase64(privBuffer);
|
||||||
|
|
||||||
|
log("New key pair generated successfully. Public key is ready for sharing.");
|
||||||
|
} catch (e) {
|
||||||
|
log("ERROR: Could not generate keys. " + e.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Imports a public key from Base64 string for ENCRYPTION. */
|
||||||
|
async function importRecipientPublicKey(b64Key) {
|
||||||
|
const keyBuffer = base64ToBuffer(b64Key.replaceAll(/\s/g, ''));
|
||||||
|
return crypto.subtle.importKey(
|
||||||
|
'spki', // Use 'spki' for X.509 public key format
|
||||||
|
keyBuffer,
|
||||||
|
ENCRYPT_ALGORITHM,
|
||||||
|
false, // not extractable
|
||||||
|
['encrypt']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Imports the user's private key from Base64 string for DECRYPTION. */
|
||||||
|
async function importUserPrivateKey(b64Key) {
|
||||||
|
const keyBuffer = base64ToBuffer(b64Key.replaceAll(/\s/g, ''));
|
||||||
|
return crypto.subtle.importKey(
|
||||||
|
'pkcs8', // Use 'pkcs8' for private key format
|
||||||
|
keyBuffer,
|
||||||
|
ENCRYPT_ALGORITHM,
|
||||||
|
false, // not extractable
|
||||||
|
['decrypt']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Imports the user's private key from Base64 string for SIGNING. */
|
||||||
|
async function importUserSigningKey(b64Key) {
|
||||||
|
const keyBuffer = base64ToBuffer(b64Key.replaceAll(/\s/g, ''));
|
||||||
|
return crypto.subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
keyBuffer,
|
||||||
|
SIGN_ALGORITHM,
|
||||||
|
false,
|
||||||
|
['sign']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- MESSAGE LOGIC ---
|
||||||
|
|
||||||
|
/** Canonicalizes the set of messages for deterministic signing. */
|
||||||
|
function getEnvelopeStringToSign(messages, publicKey) {
|
||||||
|
const messageData = messages
|
||||||
|
.map(m => m.encryptedMessage + ";" + m.senderPublicKey + ";" + m.recipientPublicKey + ";" + m.timestamp)
|
||||||
|
.sort() // CRUCIAL: Must sort for deterministic signing
|
||||||
|
.join("|");
|
||||||
|
|
||||||
|
return messageData + ";" + publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sends a message to the server. */
|
||||||
|
async function sendMessage() {
|
||||||
|
const messageContent = document.getElementById('messageContent').value.trim();
|
||||||
|
const recipientKey = document.getElementById('recipientKey').value.trim();
|
||||||
|
const senderKey = document.getElementById('publicKey').value.trim();
|
||||||
|
const privateKeyB64 = document.getElementById('privateKey').value.trim();
|
||||||
|
|
||||||
|
if (!messageContent || !recipientKey || !senderKey || !privateKeyB64) {
|
||||||
|
return log("ERROR: All fields (message, recipient key, your public/private keys) must be filled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Import Recipient's Public Key for ENCRYPTION
|
||||||
|
const pubKey = await importRecipientPublicKey(recipientKey);
|
||||||
|
|
||||||
|
// 2. Encrypt the Message
|
||||||
|
const encryptedBuffer = await crypto.subtle.encrypt(
|
||||||
|
ENCRYPT_ALGORITHM,
|
||||||
|
pubKey,
|
||||||
|
strToBuffer(messageContent)
|
||||||
|
);
|
||||||
|
const encryptedMessage = bufferToBase64(encryptedBuffer);
|
||||||
|
log("Message encrypted. Ciphertext size: " + encryptedMessage.length + " bytes.");
|
||||||
|
|
||||||
|
// 3. Construct the Message and Envelope DTOs
|
||||||
|
const message = {
|
||||||
|
encryptedMessage: encryptedMessage,
|
||||||
|
senderPublicKey: senderKey,
|
||||||
|
recipientPublicKey: recipientKey,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
};
|
||||||
|
const envelope = {
|
||||||
|
messages: [message],
|
||||||
|
publicKey: senderKey,
|
||||||
|
signature: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
// 4. Canonicalize and Sign the Envelope
|
||||||
|
const stringToSign = getEnvelopeStringToSign(envelope.messages, envelope.publicKey);
|
||||||
|
const signingKey = await importUserSigningKey(privateKeyB64);
|
||||||
|
|
||||||
|
const signatureBuffer = await crypto.subtle.sign(
|
||||||
|
SIGN_ALGORITHM,
|
||||||
|
signingKey,
|
||||||
|
strToBuffer(stringToSign)
|
||||||
|
);
|
||||||
|
envelope.signature = bufferToBase64(signatureBuffer);
|
||||||
|
|
||||||
|
log("Envelope signed. Signature length: " + envelope.signature.length + " bytes.");
|
||||||
|
|
||||||
|
// 5. Send to Server
|
||||||
|
const response = await fetch(SERVER_URL + "/receiveMessage", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(envelope)
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.text();
|
||||||
|
log(`Server Response (SEND): ${result}`);
|
||||||
|
} catch (e) {
|
||||||
|
log("FATAL ERROR during send: " + e.message);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Retrieves and decrypts messages from the server. */
|
||||||
|
async function retrieveMessages() {
|
||||||
|
const userKey = document.getElementById('publicKey').value.trim();
|
||||||
|
const privateKeyB64 = document.getElementById('privateKey').value.trim();
|
||||||
|
const incomingDiv = document.getElementById('incomingMessages');
|
||||||
|
incomingDiv.innerHTML = ''; // Clear previous messages
|
||||||
|
|
||||||
|
if (!userKey || !privateKeyB64) {
|
||||||
|
return log("ERROR: Your public and private keys must be filled to retrieve messages.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Prepare Envelope for Retrieve Request (uses an empty message set)
|
||||||
|
const envelope = {
|
||||||
|
messages: [], // Message list is empty for a retrieval request
|
||||||
|
publicKey: userKey,
|
||||||
|
signature: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Canonicalize and Sign the Envelope
|
||||||
|
const stringToSign = getEnvelopeStringToSign(envelope.messages, envelope.publicKey);
|
||||||
|
const signingKey = await importUserSigningKey(privateKeyB64);
|
||||||
|
|
||||||
|
const signatureBuffer = await crypto.subtle.sign(
|
||||||
|
SIGN_ALGORITHM,
|
||||||
|
signingKey,
|
||||||
|
strToBuffer(stringToSign)
|
||||||
|
);
|
||||||
|
envelope.signature = bufferToBase64(signatureBuffer);
|
||||||
|
|
||||||
|
// 3. Send to Server
|
||||||
|
const response = await fetch(SERVER_URL + "/retrieveMessages", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(envelope)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
return log(`Server Error (${response.status}): ${await response.text()}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = await response.json();
|
||||||
|
if (!messages || messages.length === 0) {
|
||||||
|
incomingDiv.innerHTML = "<p>No new messages found.</p>";
|
||||||
|
return log("Successfully checked for messages. None found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
log(`Retrieved ${messages.length} message(s). Attempting decryption...`);
|
||||||
|
|
||||||
|
// 4. Import User's Private Key for DECRYPTION
|
||||||
|
const privateKey = await importUserPrivateKey(privateKeyB64);
|
||||||
|
|
||||||
|
// 5. Decrypt Each Message
|
||||||
|
for (const msg of messages) {
|
||||||
|
const box = document.createElement('div');
|
||||||
|
box.className = 'message-box';
|
||||||
|
let decryptedContent = "--- Decryption Failed ---";
|
||||||
|
|
||||||
|
try {
|
||||||
|
const encryptedBuffer = base64ToBuffer(msg.encryptedMessage);
|
||||||
|
const decryptedBuffer = await crypto.subtle.decrypt(
|
||||||
|
ENCRYPT_ALGORITHM,
|
||||||
|
privateKey,
|
||||||
|
encryptedBuffer
|
||||||
|
);
|
||||||
|
decryptedContent = bufferToStr(decryptedBuffer);
|
||||||
|
} catch (e) {
|
||||||
|
log(`Decryption failed for a message from ${msg.senderPublicKey}: ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
box.innerHTML = `
|
||||||
|
<p><strong>Sender:</strong> ${msg.senderPublicKey.substring(0, 30)}...</p>
|
||||||
|
<p><strong>Timestamp:</strong> ${new Date(msg.timestamp).toLocaleString()}</p>
|
||||||
|
<p class="encrypted-message">Encrypted Data: ${msg.encryptedMessage.substring(0, 50)}...</p>
|
||||||
|
<p><strong>Decrypted Message:</strong> <span class="decrypted-message">${decryptedContent}</span></p>
|
||||||
|
`;
|
||||||
|
incomingDiv.appendChild(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(`Successfully retrieved and processed ${messages.length} message(s).`);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
log("FATAL ERROR during retrieval: " + e.message);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
617
src/main/resources/static/readme.md
Normal file
617
src/main/resources/static/readme.md
Normal file
@@ -0,0 +1,617 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# Hypernode distributed ledger system
|
||||||
|
|
||||||
|
This program aims to create a distributed ledger with the goal of illustrating the Hypernode validation algorithm.
|
||||||
|
It is a Proof of Stake system that supports delegation, can scale well, requires little power,supports multiple signing
|
||||||
|
algorithms(tested with RSA and ML-DSA-87),can relay messages, has a built-in voting system to update its own ledger parameters
|
||||||
|
and a built in DNS system.
|
||||||
|
It was written from scratch in Java using the Spring framework, as it is a very easy language to read, understand and validate.
|
||||||
|
One of the reason for the creation of this program was that the Bitcoin code was not intuitive to read or easy to deploy,
|
||||||
|
so auditability and ease of startup were important objectives of this project.
|
||||||
|
Ease of use and security were considered, as you need both a signature from the user (like chip and pin)
|
||||||
|
and from your validatorNode (which acts like a sort of bank), so that incidents like MtGox cannot happen anymore.
|
||||||
|
You then get the ease of use of having normal people operate thin clients while validatornodes do the heavy lifting,
|
||||||
|
but at the same time a motivated user could run his own validatorNode.
|
||||||
|
The power and computation were also a concern, especially as that GPU power could now be allocated to AI.
|
||||||
|
Another reason I was not fully happy with Bitcoin was the lack of an internal formal voting system, and the inability to
|
||||||
|
issue new currency without recurring to an hard fork.
|
||||||
|
Since this system is based on the delegated validatornodes to manage monetary policy this is, in fact, a democratic instrument.
|
||||||
|
If you so desire you do not even need the Internet to run this kind of ledger, you could run one entirely via avian carriers,
|
||||||
|
therefore becoming an important asset for communities that can't or won't connect to the mainstream Internet.
|
||||||
|
Despite those critiques Bitcoin has undoubtedly paved the road for a lot of these concepts, this project is more of a fine tune of a good idea according to my specific taste, requirements and my personal flavour of autism and not about throwing shade at a successful project.
|
||||||
|
|
||||||
|
# IMPORTANT:
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
This program is a proof of concept. It was not possible to test it at scale before its release to the public.
|
||||||
|
Feel free to modify it to suit your needs, but remember to share the code using AGPL
|
||||||
|
(see https://www.gnu.org/licenses/agpl-3.0.html) and to credit this project by linking back here.
|
||||||
|
If you want to turn this program into an official production system please hire someone competent to tailor the system to your needs
|
||||||
|
and validate this software by paying for a third party audit.
|
||||||
|
In the meantime feel free to start with the default code and settings, you will be able to export the ledger
|
||||||
|
and restart it onto a compatible chain at a later date if you want to.
|
||||||
|
|
||||||
|
## the Hypernode transmission algorithm
|
||||||
|
|
||||||
|
### How does it work?
|
||||||
|
|
||||||
|
<details><summary>Collapse / Expand</summary> The easiest real life example on how to visualize the Hypernode is by looking at a Rubick's Cube.<br>
|
||||||
|
The cube is composed by 27 individual smaller cubes (one central element and those who expose the colored faces).
|
||||||
|
Place the cube on the table:
|
||||||
|
if you were to slice it like an hamburger you would see that those 27 individual cubes components
|
||||||
|
can be represented this way:
|
||||||
|
|
||||||
|
Bottom (1)
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 |
|
||||||
|
|-|-|-|-|
|
||||||
|
|1 |1.1.1| 1.1.2| 1.1.3 |
|
||||||
|
|2 |1.2.1| 1.2.2| 1.2.3 |
|
||||||
|
|3 |1.3.1| 1.3.2| 1.3.3 |
|
||||||
|
|
||||||
|
Middle (2)
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 |
|
||||||
|
|-|-|-|-|
|
||||||
|
|1 | 2.1.1| 2.1.2 |2.1.3 |
|
||||||
|
|2 | 2.2.1| 2.2.2 |2.2.3 |
|
||||||
|
|3 | 2.3.1| 2.3.2 |2.3.3 |
|
||||||
|
|
||||||
|
Top(3)
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 |
|
||||||
|
|-|-|-|-|
|
||||||
|
|1 | 3.1.1| 3.1.2 |3.1.3|
|
||||||
|
|2 | 3.2.1| 3.2.2 |3.2.3|
|
||||||
|
|3 | 3.3.1| 3.3.2 |3.3.3|
|
||||||
|
|
||||||
|
We can now identify each element univocally by the value of its height, row or column.
|
||||||
|
Let's take 1.1.1 as a node that wants to propagate a message, to demonstrate the logic behind the Hypernode.
|
||||||
|
First of all let's define as peers the nodes that only differ by 1 element of the address
|
||||||
|
(in this case 1.1.2, 1.1.3, 1.2.1,1.3.1, 2.1.1, 3.1.1).
|
||||||
|
As you can see those are the elements on the same row, column or vertical as our chosen element.
|
||||||
|
All of these elements will read the data of the element, and on the next turn they can forward it too
|
||||||
|
on their own peers.
|
||||||
|
This will mean that the data of 1.1.1 is now be available to all who differ by 1 element of the address,
|
||||||
|
and at the next stage it will be available for everyone who differ with one of those peers by 1 element in the address.
|
||||||
|
This means that everyone whi differs by 2 elements now should have access to that data.
|
||||||
|
If we continue further we reach 3 iterations, and since we have 3 address values we have propagated the original message
|
||||||
|
to all of the elements of the network.
|
||||||
|
|
||||||
|
#### Progression of the message emanating from 1.1.1
|
||||||
|
|
||||||
|
Bottom
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 |
|
||||||
|
|-|-|-|-|
|
||||||
|
|1|0|1|1|
|
||||||
|
|2|1|2|2|
|
||||||
|
|3|1|2|2|
|
||||||
|
|
||||||
|
Middle
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 |
|
||||||
|
|-|-|-|-|
|
||||||
|
|1|1|2|2|
|
||||||
|
|2|2|3|3|
|
||||||
|
|3|2|3|3|
|
||||||
|
|
||||||
|
Top
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 |
|
||||||
|
|-|-|-|-|
|
||||||
|
|1|1|2|2|
|
||||||
|
|2|2|3|3|
|
||||||
|
|3|2|3|3|
|
||||||
|
|
||||||
|
Now the element 1.1.1 needs to receive the signature from the elements reached in our last iteration,
|
||||||
|
so by repeating the procedure 3 further times the original sender has propagated to everyone his message and
|
||||||
|
received the receipt signature from everyone else in the network.
|
||||||
|
At this point it is possible to calculate the hash of the resulting transaction block, and doing another pass
|
||||||
|
we can diffuse the hash value and make sure that there is a majority of the voting that has the same hash for this transaction block,
|
||||||
|
therefore validating it and using that hash as the seed for the next encryption block.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
<details><summary>Collapse / Expand</summary>
|
||||||
|
In the previous example to have a complete validated frame every node you had to contact
|
||||||
|
6 (peers) * 3 (iterations per frame) * 3 (frames per valid block), or 54 connections.
|
||||||
|
The general formula to calculate the necessary number of connections to complete a transmission event for a node is
|
||||||
|
roundup(dimensions * (servers ^ (1/dimensions) -1))
|
||||||
|
and the number of connection required to validate a block is
|
||||||
|
nodes * dimensions * 3
|
||||||
|
As you will see in the examples below a higher dimension number will reduce the required number of peers (up to a point)
|
||||||
|
but will also increase the number of iterations, making a block validation more time consuming,
|
||||||
|
and after reaching an optimum the total number of connection will rise too.
|
||||||
|
The practical way is to determine the maximum number of peers you would like to have and set the staking cost accordingly,
|
||||||
|
so you can decide how much decentralization vs performance you want to have
|
||||||
|
|
||||||
|
Top row :Exponent
|
||||||
|
first column: nodes
|
||||||
|
value: number of peers
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||||
|
|--------|-------|-----|-----|----|----|----|----|----|----|----|
|
||||||
|
| 10 | 9 | 5 | 4 | 4 | 3 | 3 | 3 | 3 | 3 | 3 |
|
||||||
|
| 100 | 99 | 18 | 11 | 9 | 8 | 7 | 7 | 7 | 7 | 6 |
|
||||||
|
| 1000 | 997 | 62 | 27 | 19 | 15 | 13 | 12 | 11 | 11 | 10 |
|
||||||
|
| 10000 | 9999 | 198 | 62 | 36 | 27 | 22 | 20 | 18 | 17 | 16 |
|
||||||
|
| 100000 | 99999 | 630 | 137 | 68 | 45 | 35 | 30 | 26 | 24 | 22 |
|
||||||
|
|
||||||
|
Top row :Exponent
|
||||||
|
first column: nodes
|
||||||
|
value: total connections per block
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||||
|
|--------|--------|------|------|-----|-----|-----|-----|-----|-----|-----|
|
||||||
|
| 10 | 27 | 30 | 36 | 48 | 45 | 54 | 63 | 72 | 81 | 90 |
|
||||||
|
| 100 | 297 | 108 | 99 | 108 | 120 | 126 | 147 | 168 | 189 | 180 |
|
||||||
|
| 1000 | 2997 | 372 | 243 | 228 | 225 | 234 | 252 | 264 | 297 | 300 |
|
||||||
|
| 10000 | 29997 | 1188 | 558 | 432 | 405 | 396 | 420 | 432 | 459 | 480 |
|
||||||
|
| 100000 | 299997 | 3786 | 1233 | 816 | 675 | 630 | 630 | 624 | 648 | 660 |
|
||||||
|
|
||||||
|
Top row :Exponent
|
||||||
|
first column: nodes
|
||||||
|
value: total connections per complete block when adding 1 redundancy transmission frame
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||||
|
|--------|--------|------|------|------|-----|-----|-----|-----|-----|-----|
|
||||||
|
| 10 | 54 | 45 | 48 | 60 | 54 | 63 | 72 | 81 | 90 | 99 |
|
||||||
|
| 100 | 594 | 162 | 132 | 135 | 144 | 147 | 168 | 189 | 210 | 198 |
|
||||||
|
| 1000 | 5994 | 558 | 324 | 285 | 270 | 273 | 288 | 297 | 330 | 330 |
|
||||||
|
| 10000 | 59994 | 1782 | 744 | 540 | 486 | 462 | 480 | 486 | 510 | 528 |
|
||||||
|
| 100000 | 599994 | 5679 | 1644 | 1020 | 810 | 735 | 720 | 702 | 720 | 726 |
|
||||||
|
|
||||||
|
Top row :Exponent
|
||||||
|
first column: peers
|
||||||
|
value: max number of nodes of the system
|
||||||
|
|
||||||
|
| | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||||
|
|-----|-----|------|-------|--------|---------|----------|-----------|-----------|------------|-------------|
|
||||||
|
| 10 | 11 | 36 | 64 | 81 | 243 | 64 | 128 | 256 | 512 | 1024 |
|
||||||
|
| 20 | 21 | 121 | 343 | 1296 | 3125 | 4096 | 2187 | 6561 | 19683 | 59049 |
|
||||||
|
| 50 | 51 | 676 | 4913 | 28561 | 161051 | 531441 | 2097152 | 5764801 | 10077696 | 60466176 |
|
||||||
|
| 100 | 101 | 2601 | 39304 | 456976 | 4084101 | 24137569 | 170859375 | 815730721 | 5159780352 | 25937424601 |
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Project contents
|
||||||
|
|
||||||
|
### Project structure
|
||||||
|
|
||||||
|
<details><summary>Collapse / Expand</summary>
|
||||||
|
|
||||||
|
This project contains all the logic required to keep the distributed ledger running.
|
||||||
|
- DistributedLedgerApplication, starting point of the Spring system (mostly empty)
|
||||||
|
- ErrorHandling, used to have a hook point to log the exceptions while keeping the system running
|
||||||
|
|
||||||
|
#### controller
|
||||||
|
|
||||||
|
- WebServiceEndpoints, used to expose the Endpoints, called by WebServiceCaller and processed in WebServiceEngine
|
||||||
|
- ClientEndpoints, used to expose the endpoints used by the ClientEngine
|
||||||
|
- EncryptionEndpointController, used to mock up the endpoints of an external encryption entity server
|
||||||
|
|
||||||
|
#### webService
|
||||||
|
|
||||||
|
- WebServiceEngine, contains the core logic of the system
|
||||||
|
- WebServiceInitializer, used only at startup to configure the WebServiceEngine
|
||||||
|
- WebServiceCaller, helper class of static methods used to call a WebService endpoint
|
||||||
|
|
||||||
|
#### EncryptionInterface
|
||||||
|
|
||||||
|
- Encryption, helper class of static methods used to perform encryption operations
|
||||||
|
- EncryptionEntity_BaseInterface, the interface to implement if you want to create a new encryption device
|
||||||
|
(like a smart card reader or to connect to a signature server)
|
||||||
|
- EncryptionEntity_Integrated, a base implementation of a local system that can sign a message.
|
||||||
|
Would recommend to use a different system if your network starts to have significant value,
|
||||||
|
as it is not best practice to store your private key in the JVM memory.
|
||||||
|
- EncryptionEntity_AuthenticationServer, a bridge class that allows you to store your keys in a different server,
|
||||||
|
significantly improving security.
|
||||||
|
|
||||||
|
#### Client
|
||||||
|
|
||||||
|
- ClientEngine, used to showcase how a client is supposed to connect and interact with the network
|
||||||
|
- ExternalPayment, used to allow the client to schedule a payment and spend his money
|
||||||
|
|
||||||
|
#### Contracts
|
||||||
|
|
||||||
|
- AccountAttributesUpdate
|
||||||
|
- BlockRevisionResult
|
||||||
|
- DistributedLedgerAccount
|
||||||
|
- LedgerHostory
|
||||||
|
- LedgerParameters
|
||||||
|
- Payment
|
||||||
|
- Signature
|
||||||
|
- SignedvalidatorMessage
|
||||||
|
- StatusDataContract
|
||||||
|
- TransportMessageDataContract
|
||||||
|
- ValidatorMessageDataContract
|
||||||
|
- ValidatorNode
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Program lifecycle:
|
||||||
|
|
||||||
|
<details><summary>Collapse / Expand</summary>
|
||||||
|
|
||||||
|
#### Pre-lauch setup
|
||||||
|
|
||||||
|
You will need the Java 24 virtual machine on your system.
|
||||||
|
To launch the problem you can run this command on the command line
|
||||||
|
`java -jar /path/to/file/ledger.jar`
|
||||||
|
or if you want to specify a port that is not 8080 you can use
|
||||||
|
`java -jar /path/to/file/ledger.jar --server.port=8081`
|
||||||
|
or if you want to use quantum safe encryption you can run it with
|
||||||
|
`java -jar /path/to/file/ledger.jar --server.port=8081 --encryption.keyAlgo=ML-DSA-87 --encryption.signAlgo=ML-DSA-87`
|
||||||
|
|
||||||
|
It is important to use nginx to only forward specific endpoints and keep the management of the app in local.
|
||||||
|
The endpoints to forward are contained in the /hdls/ subfolder (Hypernode Distributed Ledger System)
|
||||||
|
/hdls/requestAuthenticationStringToSign
|
||||||
|
/hdls/authenticateServer
|
||||||
|
/hdls/initialize
|
||||||
|
/hdls/AccountTotals
|
||||||
|
/hdls/getAmount
|
||||||
|
/hdls/getStatus
|
||||||
|
/hdls/spend
|
||||||
|
/hdls/updateAccountAttributes
|
||||||
|
/hdls/getBlockId
|
||||||
|
/hdls/getLedgerHistory
|
||||||
|
/hdls/getCurrentlyTransmittedTransportMessage
|
||||||
|
/hdls/getAccountInfo
|
||||||
|
/hdls/getIPAddress
|
||||||
|
|
||||||
|
#### Initializing the server
|
||||||
|
|
||||||
|
You can use this program to connect to an existing distributed ledger or to create your own new ledger.
|
||||||
|
First of all assign an EncryptionEntity
|
||||||
|
(endpoint WebServiceEndpoints.setEncryptionEntityIntegrated(), webpage /hdls-server-admin/setEncryptionEntityIntegrated)
|
||||||
|
you can either generate a key pair from Terminal with ssh-keygen or call /hdls-client/newKeyPair.
|
||||||
|
|
||||||
|
##### Connect to an existing ledger:
|
||||||
|
|
||||||
|
Call the endpoint InitAndConnectToExistingLedger
|
||||||
|
(endpoint WebServiceEndpoints.initAndConnectToExistingLedger(), webpage /hdls-server-admin/createNewLedger)
|
||||||
|
to connect to the specified target node.
|
||||||
|
A template is included in the webpage, you will need to specify your address and your target server's address.
|
||||||
|
Please use the IP address instead of domain names (i.e. http://1.2.3.4/ instead of http://mysite.net)
|
||||||
|
|
||||||
|
##### Create a new ledger:
|
||||||
|
|
||||||
|
Call the endpoint InitCreateNewLedger
|
||||||
|
(endpoint WebServiceEndpoints.initCreateNewLedger(), webpage /hdls-server-admin/joinExistingLedger),
|
||||||
|
specifying the parameters in the initial StatusDataContract.
|
||||||
|
You will then be able to connect to this server from another instance of this program as described in "Connect to an existing ledger".
|
||||||
|
A template is included in the default webpage, you will need to replace the address placeholders with the actual addresses.
|
||||||
|
Alternatively you could import a previous StatusDataContract and create a fork of someone else's ledger
|
||||||
|
|
||||||
|
#### Client Operation
|
||||||
|
|
||||||
|
Once the network is initialized it is possible to register transactions
|
||||||
|
|
||||||
|
##### Managing private keys
|
||||||
|
|
||||||
|
Every account holder can access the system through a client connected to one of the nodes of the ledger.
|
||||||
|
This client needs to utilize an EncryptionInterface, like a chip and pin, a dedicated signable server
|
||||||
|
or the integrated app option.
|
||||||
|
This EncryptionInterface needs to sign messages and show its public key,
|
||||||
|
to verify that it is really the user doing the authorization.
|
||||||
|
You can generate a private and public key by calling the endpoint /hdls-client/newKeyPair, and the public key is effectively
|
||||||
|
the account name and the privateKey is the equivalent of the password.
|
||||||
|
If your currency starts to gain value I'd recommend to switch to chip&pin systems to avoid keeping the key
|
||||||
|
in the JVM memory, but this is not really a concern when starting out a low value ledger.
|
||||||
|
You can find a code sample to instantiate an EncryptionInterface in the Test area of the project
|
||||||
|
|
||||||
|
##### Submit a payment
|
||||||
|
|
||||||
|
The object ExternalPayment represents a payment as it needs to be compiled by the client.
|
||||||
|
It does specify the sender and the recipient key (in base64) or his friendly name, the comment, which block is going to be processed in,
|
||||||
|
the amount to be paid and the signature of the message itself.
|
||||||
|
The resulting payments can be grouped into a list and communicated to the validatornode by calling the endpoint /hdls/spend.
|
||||||
|
|
||||||
|
##### Update Account Attributes
|
||||||
|
|
||||||
|
If your current validatorNode is not representing you correctly you can choose to change it.
|
||||||
|
Contact any other validatorNode at the /hdls/votedelegation endpoint, and sign your desire to change your delegation.
|
||||||
|
You could even point the vote to yourself, and if you have enough votes log in as a validatornode as specified on point 1a.
|
||||||
|
The same process can be used to change your public friendly name, if the name you have chosen is not already taken.
|
||||||
|
|
||||||
|
##### Retrieve account information
|
||||||
|
|
||||||
|
The endpoint /hdls/getAccountInfo will provide you with all the informations associated with a specific name or public key,
|
||||||
|
if you only want to find out the IP address for a specific public key (or for a specific name) you can call /hdls/getIPAddress
|
||||||
|
|
||||||
|
#### Server operation
|
||||||
|
|
||||||
|
##### WebService State
|
||||||
|
|
||||||
|
The WebService state is determined by:
|
||||||
|
StatusDataContract, which contains the results after the last agreed upon block
|
||||||
|
TransportMessageDataContract, which is the block currently being processed
|
||||||
|
PendingNextMessage, which stores the transactions that will be discussed in the next block
|
||||||
|
List of ValidatorNodes, which contains the references to the other peers in the network
|
||||||
|
|
||||||
|
##### Processing loop
|
||||||
|
|
||||||
|
- Setting up the data
|
||||||
|
Once one block is verified, the quest to establish the next block begins.
|
||||||
|
First of all the peers take the transactions stored in their PendingNextMessage,
|
||||||
|
they move it into the TransportMessageDataContract, which will then be copied into
|
||||||
|
currentlyTransmittedTransportMessage and exposed for every peer to see.
|
||||||
|
- Exposing the data
|
||||||
|
currentlyTransmittedTransportMessage is then made available by calling a GET endpoint /hdls/getCurrentlyTransmittedTransportMessage.
|
||||||
|
This has the advantage that it is way more resilient to DDOS as the HTTP request can be distributed
|
||||||
|
by a CDN, using a load balancer to forward the original HTTP message to a pool of servers.
|
||||||
|
It can also be used to detach the machines sharing the data from the machine that's actually doing the processing.
|
||||||
|
Once enough time (as specified in the LedgerParameters) has passed every node will show both his own messages
|
||||||
|
and the messages they gathered by the peers, behaving like a p2p network and signing other's messages.
|
||||||
|
This is important because it is by others relaying the message of other peers that we are able to validate
|
||||||
|
that one specific node has been acknowledged by the whole network.
|
||||||
|
- Propagating messages and read receipts
|
||||||
|
At regular intervals (specified in the LedgerParameter) the message that is exposed in the /hdls/getCurrentlyTransmittedTransportMessage
|
||||||
|
is updated,and a new read is performed on this node's peers, as illustrated in the Explaining the Hypernode section.
|
||||||
|
We do add one extra turn of message exchange, so that if one of your contacts is offline you would still receive
|
||||||
|
the messages he's supposed to carry.
|
||||||
|
Once we have accumulated every message and every read receipt we can start the validation process.
|
||||||
|
- Validation and block processing
|
||||||
|
Every validator node is weighted by the amount delegated toward them.
|
||||||
|
At the end of the message exchange it is time to sum the votes (accounting for delegation).
|
||||||
|
To do so you verify which elements you possess with their message, and which receipt signatures they signed.
|
||||||
|
You then establish which elements will need to be banned (due to invalid or doubled messages) and once removed
|
||||||
|
you can compute the hash of the map<publickey,messages>. This is done in BlockRevisionResult.
|
||||||
|
If least 50% + 1 agree on the same signature then it becomes the new founding block, the hash is written
|
||||||
|
and is used to validate the new frame, linking it back to it to make it more difficoult to pre compute hashes.
|
||||||
|
That hash will also be used by the validatornode when paying, so that every payment works like a cheque
|
||||||
|
and the validator will act like the bank teller, signing it on behalf of the bank. This makes it possible to
|
||||||
|
make payments in advance, and maintaining it securely because only the delegated validatornode can authorize the spend.
|
||||||
|
If your node disagrees it will stay in that frame, allowing you to decide of you want to reattach to the main network
|
||||||
|
or if you want to create a fork with the other dissidents.
|
||||||
|
- Voted parameter changes
|
||||||
|
It is possible for the validator nodes to change the network LedgerParameters values as well as to reassign the amount in an account,
|
||||||
|
if more than 50% of the voting delegation agrees to it.
|
||||||
|
This is how you can "print money" and finance your community endeavors.
|
||||||
|
Call the endpoint /hdls/changeVotedParameters to pass the desired parameters, and keep an eye on the transaction history
|
||||||
|
(as you need to coordinate, because only when you get over 50% of the votes the system changes setup)
|
||||||
|
|
||||||
|
#### Tips for a better experience
|
||||||
|
|
||||||
|
This system uses the delegated amounts to express votes. This means that the richer you are the more influence you will have
|
||||||
|
in the voting parts of the program. This is balanced by the fact that it is not the only "voting" system, as this system
|
||||||
|
is very easy to fork. If the community believes you are not operating in the interest of the whole community
|
||||||
|
they can create a fork and, for all intents and purposes, operating on the forked system is a vote to kick you out of the community.
|
||||||
|
To enhance the robustness of the network do not assign more than 3% of the total votes to the same validator node.
|
||||||
|
Use a CDN/ load balancer to expose the /hdls/getCurrentlyTransmittedTransportMessage, so the computer doing the calculations is not directly exposed to the web.
|
||||||
|
Use some sort of VPN or similar to limit the amount of people who could DDoS your server, maybe even write an interface
|
||||||
|
so that those gathering data can be blocked by a login page that is disconnected from the core system.
|
||||||
|
Ideally the private key and public key should be implemented in a chip and pin card or equivalent, making key extraction
|
||||||
|
something not really possible. If you cannot do that it is wise to use multiple private keys, so that if you are unlucky
|
||||||
|
enough to get your key guessed or your computer hacked you will still have the rest of your money.
|
||||||
|
If you want to run multiple instances (maybe to help different currencies) you can use nginx to route different subdomains
|
||||||
|
(i.e. currency1.wallethost.net, currency2.wallethost.net, currency3.wallethost.net) to different instances of the java applet
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Managing a community
|
||||||
|
|
||||||
|
### Basic concepts of central banking
|
||||||
|
|
||||||
|
<details> <summary>Collapse / Expand</summary>
|
||||||
|
|
||||||
|
This program aims to provide small communities with the ability to manage their own currency.
|
||||||
|
If you have read this far in the file you have the knowledge of the technical instrument,
|
||||||
|
but you also need to understand the basics of Central Banks and monetary policy
|
||||||
|
in order to give value to your community's currency
|
||||||
|
|
||||||
|
#### What is a currency?
|
||||||
|
|
||||||
|
Currencies are a tool created to facilitate the exchange of goods and services.
|
||||||
|
One of the first instances of currency were clay tablets near Babylon, which were claims on next year’s grain harvest
|
||||||
|
Historically precious metals have been used, but the discovery of gold and silver mines could crash your economy at any time
|
||||||
|
and you are wasting manpower to extract something which is only used symbolically.
|
||||||
|
Recently cryptocurrencies have offered an alternative to those systems, by maintaining a decentralized ledger of performed payments.
|
||||||
|
Ultimately the field of economics has observed that being able to emit money in a way that maintains a small price growth (between 2% and 4% /year)
|
||||||
|
improves economic activity, minimises unemployment and creates confidence in the economy and its managerial class
|
||||||
|
|
||||||
|
#### What gives value to a currency?
|
||||||
|
|
||||||
|
There are various interpretations of this question.
|
||||||
|
Some believe that the currency has value because it buys things, which is true but partial.
|
||||||
|
The reality is that a currency is a way for a community to maintain a ledger
|
||||||
|
and it is the access to the participation of that community that makes the currency valuable.
|
||||||
|
Try bringing Canadian Dollars into Europe: there won't be much interest in them because
|
||||||
|
the Canadian community is not easily accessible, while it is considerably easier to
|
||||||
|
pay someone in Canada with USD, as the Canadian community is closer and has regular ties with the US
|
||||||
|
|
||||||
|
#### How can you create a currency out of thin air?
|
||||||
|
|
||||||
|
First of all you need a community of some sort.
|
||||||
|
You can then mint “money†to reward members of the community who perform services for the community itself.
|
||||||
|
For example lets say that your community has 10.000 people and needs to pay one doctor, one judge and the central banker / mayor
|
||||||
|
Those three people are effectively community employees, so your community can decide to “print†their salary, which they will accept
|
||||||
|
as long as they know that they will be able to trade their salary with services and necessities within the economy of that community.
|
||||||
|
That money will be more or less valuable depending on what the free citizens in the private sector can provide in exchange.
|
||||||
|
The act of balancing the amount of public expenses with how much money to print and how much tax revenue to demand
|
||||||
|
is the skill required to be the economics minister, as there is a trilemma to be managed:
|
||||||
|
- too much taxation means reducing productivity and it does incentivise the black market, and will encourage the most honest productive people to leave
|
||||||
|
- too much money printing creates inflation, which cannot always be mitigated via taxation (going that route creates imbalance between public and private sector)
|
||||||
|
- too few public expenses create discontent in the population, which might decide to leave your community and join a different community
|
||||||
|
In conclusion, currency is created when the private sector decides to live in a community where the correct amount of public services are provided
|
||||||
|
balancing low taxes, high services and a stable currency
|
||||||
|
|
||||||
|
#### How do you start the circulation of your currency?
|
||||||
|
|
||||||
|
If you want to make a big purchase, or get store credit to make a series of small purchases, you will need to transfer money to a recipient.
|
||||||
|
It is the prerogative of every community to decide how to initiate the process, but here are some possible suggestion:
|
||||||
|
1) pay the people doing community work with this currency with the promise of accepting it back with your business
|
||||||
|
2) those who have money in this system can extend some credit to potential new members, by granting a personal loan or monetary gift
|
||||||
|
3) create or buy something for the community using your own money or effort, and make it available for those who use that currency
|
||||||
|
4) establish a small community of 5/10 people who use this currency to settle your exchanges, and allow people you like to join your community
|
||||||
|
5) as a group of people build something together, and then reward each other with a new print of currency that keeps track of the contributions of each
|
||||||
|
</details>
|
||||||
|
|
||||||
|
### Some considerations about government
|
||||||
|
|
||||||
|
<details><summary>Collapse / Expand</summary>
|
||||||
|
|
||||||
|
To implement your own currency means implementing a system for your community,
|
||||||
|
which inevitably will evolve into a government as you will need to resolve disputes to maintain unity within that community.
|
||||||
|
Failure to do so will disintegrate the community, and the currency that represented it will soon follow the same fate.
|
||||||
|
Those below are all suggestions, you do not have to follow them to use this program,
|
||||||
|
but they could be a good starting point and a way to maintain good relations with your neighbours.
|
||||||
|
|
||||||
|
#### Internal policy
|
||||||
|
|
||||||
|
A tax revenue of 26% of GDP has historically been the most effective in developed economies (and could be a sensible starting point)
|
||||||
|
because it encourages and rewards private enterprise while there are still enough money to build critical infrastructure.
|
||||||
|
This means that people are more willing to hire a professional than doing something by themselves in an inefficient way.
|
||||||
|
(i.e. If the restaurant owner can make cheaper prices with a 26% taxation and I can eat out 5 times a week
|
||||||
|
then the tax revenue is higher compared to a situation where the tax rate is at 50% and I go there once a week.
|
||||||
|
This also saves time, which I can use to do something at which I'm more effective, increasing the GDP and therefore tax revenue.
|
||||||
|
This principle combines the concept of "velocity of money" and "specialization", which sparked the industrial revolution).
|
||||||
|
This level of taxation also has the benefit of limiting the size of government,
|
||||||
|
because after a certain size every system loses efficiency (see Group intercommunication formula: n(n - 1)/2).
|
||||||
|
If you set up a fair market system those who are closer to their own problems and have more interest to fix it will often do it by themselves.
|
||||||
|
VAT is the best way to tax, as it can be automated and can be applied to everything, is only paid once you receive money,
|
||||||
|
does not artificially stop people from working (unlike income tax thresholds)
|
||||||
|
allows old people to keep living in their own homes until they die (unlike property taxes)
|
||||||
|
and while it can be postponed it cannot be avoided.
|
||||||
|
Every law has a cost, so try to write them only when they are absolutely necessary. Try to prune your laws every 10/20 years.
|
||||||
|
Balancing taxation, money printing, laws and ethics is not easy, but if your community is united
|
||||||
|
it is going to be easier to find a common agreement on what to do.
|
||||||
|
The best moral guide to unite a community is to try to create a good place for your children to grow
|
||||||
|
so that they can start their own families, become good men and women and be ready to defend their own children.
|
||||||
|
This forces your community to think for the future and not waste your energy on temporary good times
|
||||||
|
and it makes painfully obvious who's going to pay the price for dumb choices.
|
||||||
|
|
||||||
|
#### Foreign policy
|
||||||
|
|
||||||
|
It is easy to be tolerant with the things you agree, less easy to tolerate things that disgust you.
|
||||||
|
Some of those disagreement can be solved if you admit that you have differences and you can agree to leave each other alone.
|
||||||
|
Being honest with this and adopting a federal (or special district) system can save a lot of police costs,
|
||||||
|
but you have to keep the federal entities in check and give them only the minimum level of authority.
|
||||||
|
It should always be possible to leave the federation if things are going badly.
|
||||||
|
The federal system can also turn a potential enemy into a potential ally, as long as both respect treaties, show integrity and
|
||||||
|
establish proper channels to maintain good relationships and maintain the option to stop contact if things start to go badly.
|
||||||
|
When inquiring if something is immoral and requires intervention ask yourself if all the people involved
|
||||||
|
are consenting to what is happening, and if they really have the ability to say stop.
|
||||||
|
The real end of slavery happens when people can stand on their own legs. It is the only way that you can express true consent.
|
||||||
|
Unless people make a conscious effort they will perpetrate what they perceive is being done to them,
|
||||||
|
and a lot of our human history has been about subjugation.
|
||||||
|
To help others gain autonomy is therefore a gift that will keep on giving.
|
||||||
|
Do not trust those who look like they are helping if they are creating a situation where you become dependent on them.
|
||||||
|
If you want to help other communities to stand on their own legs you can
|
||||||
|
contribute to Free Software, organize courses in your spare time, read books to be more competent,
|
||||||
|
be an active part of your community, be available if someone is trying to grow
|
||||||
|
and, most importantly, make a point to refuse to engage with those who do not respect consent.
|
||||||
|
Let's help each other become free people.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
# Credits
|
||||||
|
|
||||||
|
<details> <summary>Collapse / Expand</summary>
|
||||||
|
|
||||||
|
The dev team, for the reasons illustrated in this readme, has decided that the best way to release this program
|
||||||
|
is to license it under a Free Software license, and to remain anonymous.
|
||||||
|
This dev team does not seek profit nor fame, but in case further communications were needed
|
||||||
|
consider adding those public keys into your ledger, so that by virtue of spending a payment the dev team
|
||||||
|
will have the option to communicate with your community using the payment comments
|
||||||
|
|
||||||
|
Shinji Ikari
|
||||||
|
"MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAunH4nCceRoBkLW1Ec0j4plIxBnUVH6LcPRk2s5wGOrW8SP7gdMHH2wywh7fVTH8GWo0VLr/9Wz1kyS8GWm3XOTmlX0YEyz1ogHBSy4Sp2cDls5XMX9ct4SssMoz6EU+tkHcNYHyM0KUd8hvynrT7YTbCxSWJKmz+Wez1K93wb+jV6pi4tH9x0+4fyxq38Z3z/MK7U/D00neOCMQGB7KH0qUrTG6ZEB57X4h1frOmmwvBqnQiGDDdsVf+yHJEvbPJjoCshg8l3O3TxqZutzEup7D2tPDKkm8xmRGl1FqoWaHoa86urW1+JOrIpaUhujLZWgGUuKv6yyF47MdKV4PbQMDmbHAXhLM81wbnnv7nYpNIOwTA93OXjHPfKH2EkCP2a7U/qz9H6Oe7lKyd9oxkbcdXxU2+bbtO94mgBPsIewhspBuUWyqgXeJo8ByI73j6ynow0vIc7v8EHRuLPgAZl2/zmXuwIChYf46w8auNeMV68aNll+Au1ILE5SGy3kHRAgMBAAE="
|
||||||
|
|
||||||
|
Auron FX
|
||||||
|
"MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAk5GJ451dsBR9rPqxDmWEOab/FVIYItP2O8JHjCe6Pfvf+73vJuXIRLs+yZxohAp++zqmguo1gXJHZsVn4CVvEWlmitOCm4U7P3HugOXtiQJjBpK0U9gz+4s+qMSBEE+zSTZq+1QCUXESzoQ/+bGfnYTsH7xYk2BqexCwi4TAxSLclqJrDRGkRX8AynR4A8KjnJZjlFmtYaHNalpVuLMIorRd5IEYhIlf/mgmFyJwSpcJq+PozR7yDZFrMg2388e1Z4N8DjcxlaDpEguzdRCpd6pRW9Akb1jvfOlZJ4g394+cvz3ybrRM4jv4BfCyWicrG6THPz/ACz8YOIqGqcM3M/JdtKUe7aoxYwY+CzHA+1lyaJxigjU16uesAmv4pqxtqlTspeIiYzj5WoW3QpCWD1sC+amlDo2bpz8l3N5gFLqOaX2LAl8NYAOOPOHcHkp7Lj9/BUHV9uCpbLYxO8NFD8ubTpFooaKR6g9MCdZ4zTF6SxkRuk0l5wfANSUIYoxvAgMBAAE="
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
# FAQs
|
||||||
|
|
||||||
|
<details> <summary>Collapse / Expand</summary>
|
||||||
|
|
||||||
|
#### How does this system handle slashing conditions?
|
||||||
|
|
||||||
|
The penalty for double signing is to burn the whole amount associated with that key.
|
||||||
|
This is because in order to sign a valid message the key is likely compromised.
|
||||||
|
Downtime or censorship are punished makes the node disconnect from the network,
|
||||||
|
and given the network joining mechanism it means that they will be offline for at least two turns.
|
||||||
|
|
||||||
|
#### Why did you use Java 24 and not Java 21?
|
||||||
|
|
||||||
|
Java 24 offers support for quantum resistant cryptography.
|
||||||
|
If you are ok with RSA you can rebuild the project in Java 21 by commenting out the references to ML-DSA-87
|
||||||
|
|
||||||
|
#### How are conflicts resolved if 50% +1 agreement isn't reached?
|
||||||
|
|
||||||
|
If a consensus cannot be established right now the system is designed to continue propagating
|
||||||
|
messages until the consensus is reached.
|
||||||
|
This allows humans to step in, evaluate why there is a disagreement and allow the humans to take the latest
|
||||||
|
valid status, start a new network without the malicious players and effectively secede from the network.
|
||||||
|
Of course this issue is more "political" than technical (why is your program running maliciously)
|
||||||
|
therefore the solution must be political
|
||||||
|
|
||||||
|
#### Does this system handle Sybil attacks?
|
||||||
|
|
||||||
|
Validator onboarding stake is defined in LedgerParameters (community-adjustable).
|
||||||
|
Trade-off: High stake = more security but less decentralization.
|
||||||
|
(e.g., minStake = totalSupply / 1000 for 0.1% stake).
|
||||||
|
Communities can vote to change this parameter at any time to tailor the system to their needs.
|
||||||
|
|
||||||
|
#### Is the Hypernode efficiency really at 3k^2 * n^(1/k)?
|
||||||
|
|
||||||
|
Yes, but with some caveats.
|
||||||
|
At realistic scale (10.000 validator nodes) it will use an exponent of 3 or 4.
|
||||||
|
Using higher dimensions is beneficial for the number of connections,
|
||||||
|
but it will demand one extra messaging frame per dimension.
|
||||||
|
This is why the system asks you in the LedgerParameters how many connections can you handle
|
||||||
|
in a single messaging window, and he will calculate the correct exponent for that network load
|
||||||
|
|
||||||
|
#### Can you do anonymous transactions?
|
||||||
|
|
||||||
|
While some cryptocurrencies use Zero Knowledge Proofs to maintain anonymity, this project does not.
|
||||||
|
It is easier to just buy an acccount with some money in it, and then use that account to pay
|
||||||
|
a different vendor to buy a different clean account, so no single entity has the full transaction chain.
|
||||||
|
This is a low-tech solution, but it helps to keep the codebase small and auditable.
|
||||||
|
|
||||||
|
#### Can I use EncryptionEntity_Integrated in production?
|
||||||
|
|
||||||
|
You can do what you want, I am not your dad.
|
||||||
|
However EncryptionEntity_Integrated is considered not production-safe because the private key
|
||||||
|
is stored in the same memory as the rest of the program.
|
||||||
|
In practical terms you can use it to familiarize yourself with the program,
|
||||||
|
however if your community has more than 20.000 $ under management it might start to be risky.
|
||||||
|
To recap:
|
||||||
|
EncryptionEntity_Integrated (in-memory keys) is indeed risky for large networks.
|
||||||
|
EncryptionEntity_BaseInterface is secure if implemented correctly (e.g., wrapping an HSM/chip-and-PIN).
|
||||||
|
|
||||||
|
Here is a table comparing the options:
|
||||||
|
|
||||||
|
| Method | Use Case | Security | Example |
|
||||||
|
|---------------------------------------|--------------|-----------------|----------------------|
|
||||||
|
| EncryptionEntity_Integrated | Low value | (JVM memory) | Demo wallets |
|
||||||
|
| EncryptionEntity_AuthenticationServer | Medium value | (Remote server) | Corporate validators |
|
||||||
|
| EncryptionEntity_BaseInterface + HSM | High value | (Hardware) | Chip-and-PIN, HSM |
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
# Further links
|
||||||
|
|
||||||
|
<details> <summary>Collapse / Expand</summary>
|
||||||
|
|
||||||
|
[Latest version of this project](https://github.com/JauntyJackalope351/hypernode-ledger) - The main public repo
|
||||||
|
[Latest version of this readme](https://github.com/JauntyJackalope351/hypernode-ledger/blob/main/readme.md) - The last version of the project documentation.
|
||||||
|
[Quick start guide](https://github.com/JauntyJackalope351/hypernode-ledger/blob/main/quickstart.pdf) - Illustrated documentation on how to operate the program
|
||||||
|
[Community bootstrap guide (AI generated)](https://github.com/JauntyJackalope351/hypernode-ledger/blob/main/community_bootstrap.md) - Some ideas on how to start your own community
|
||||||
|
[Java applet](https://github.com/JauntyJackalope351/hypernode-ledger/blob/main/ledger.jar) - The actual Java application -
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
53
src/main/resources/static/script.js
Normal file
53
src/main/resources/static/script.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Sends a JSON payload to a specified endpoint.
|
||||||
|
*
|
||||||
|
* @param {string} endpoint The URL endpoint to send the JSON to.
|
||||||
|
* @param {string} jsonPayload The JSON string to send in the request body.
|
||||||
|
* @param {HTMLElement} responseArea The DOM element to display the response message.
|
||||||
|
*/
|
||||||
|
async function sendJson(endpoint, jsonPayload, responseArea) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: jsonPayload
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if the response was successful (status code 2xx)
|
||||||
|
if (!response.ok) {
|
||||||
|
// If not successful, try to read error message from response
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.text(); // assuming your controller returns plain text
|
||||||
|
responseArea.textContent = result;
|
||||||
|
responseArea.style.color = 'green'; // Set success color
|
||||||
|
} catch (error) {
|
||||||
|
responseArea.textContent = "An error occurred: " + error.message;
|
||||||
|
responseArea.style.color = 'red'; // Set error color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an event listener to the form to call sendJson when submitted
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
if (form) {
|
||||||
|
form.addEventListener('submit', async (event) => {
|
||||||
|
event.preventDefault(); // Prevent default form submission
|
||||||
|
|
||||||
|
const jsonInput = document.getElementById('jsonInput').value;
|
||||||
|
const responseArea = document.getElementById('responseMessage');
|
||||||
|
const endpoint = form.dataset.endpoint; // Get the endpoint from a data attribute on the form
|
||||||
|
|
||||||
|
if (endpoint) {
|
||||||
|
await sendJson(endpoint, jsonInput, responseArea);
|
||||||
|
} else {
|
||||||
|
responseArea.textContent = "Error: Endpoint not defined for the form.";
|
||||||
|
responseArea.style.color = 'red';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user