smarter store update after replacing chains

concurrent mining
validity checks for new transactions/blocks/metadata
rdf store stores quads
smarter clearing of transactions from pool when a new block is mined
change querying to be readonly and generic
moved some things around
This commit is contained in:
Josip Milovac 2022-12-20 11:26:06 +11:00
parent ea81105df6
commit b4c2a0c88d
7 changed files with 373 additions and 177 deletions

View file

@ -8,7 +8,11 @@ class Block {
this.hash = hash;
this.data = data;
this.nonce = nonce;
this.difficulty = difficulty || DIFFICULTY;
if (difficulty === undefined) {
this.difficulty = DIFFICULTY;
} else {
this.difficulty = difficulty;
}
}
toString() {
@ -24,26 +28,20 @@ class Block {
static genesis() {
return new this('Genesis time', '-----', 'f1r57-h45h', [], 0, DIFFICULTY);
}
//we want this to eventually be continously running where there are things in the pool,
//however as node is single threaded, this almost has to be a fiber, and yield after every
//other iteration to allow for meaningful forward progress
//we can either add all new transactions into the block as we see them, or stay with the starting list, idk which
//to be done later
static mineBlock(lastBlock, data) {
let hash, timestamp;
const lastHash = lastBlock.hash;
let { difficulty } = lastBlock;
let nonce = 0;
//returns false if hash doesn't match
static checkHash(hash, timestamp, lastHash, data, nonce, difficulty) {
const computedHash = Block.hash(timestamp, lastHash, data, nonce, difficulty);
do {
nonce++;
timestamp = Date.now();
difficulty = Block.adjustDifficulty(lastBlock, timestamp);
hash = Block.hash(timestamp, lastHash, data, nonce, difficulty);
} while (hash.substring(0, difficulty) !== '0'.repeat(difficulty));
if (computedHash !== hash) {
return false;
}
return new this(timestamp, lastHash, hash, data, nonce, difficulty);
if (hash.substring(0, difficulty) !== '0'.repeat(difficulty)) {
return false;
}
return true;
}
static hash(timestamp, lastHash, data, nonce, difficulty) {
@ -55,11 +53,16 @@ class Block {
return Block.hash(timestamp, lastHash, data, nonce, difficulty);
}
//returns false if block's hash doesn't match internals
static checkBlock(block) {
return Block.checkHash(block.hash, block.timestamp, block.lastHash, block.data, block.nonce, block.difficulty);
}
static adjustDifficulty(lastBlock, currentTime) {
let { difficulty } = lastBlock;
difficulty = lastBlock.timestamp + MINE_RATE > currentTime ?
difficulty + 1 : difficulty - 1;
return difficulty;
return Math.max(0, difficulty);
}
}

View file

@ -5,15 +5,37 @@ class Blockchain {
this.chain = [Block.genesis()];
}
addBlock(data) {
const block = Block.mineBlock(this.chain[this.chain.length-1], data);
this.chain.push(block);
//adds an existing block to the blockchain, returns false if the block can't be added, true if it was added
addBlock(newBlock) {
if (newBlock.lastHash !== this.chain[this.chain.length - 1].hash) {
console.log("Tried to add invalid block, last hash didn't match our last hash");
return false;
}
//how to check if new block's timestamp is believable
if (newBlock.difficulty !== Block.adjustDifficulty(this.chain[this.chain.length - 1], newBlock.timestamp)) {
console.log("Tried to add invalid block, difficulty is incorrect");
return false;
}
if (!Block.checkBlock(newBlock)) {
console.log("Tried to add invalid block, block's hash doesn't match its contents");
return false;
}
return block;
this.chain.push(newBlock);
console.log("Added new block: ");
//console.log(newBlock);
return true;
}
isValidChain(chain) {
if(JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis())) return false;
if (chain.length === 0) {
return false;
}
if (JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis())) {
return false;
}
for (let i=1; i<chain.length; i++) {
const block = chain[i];
@ -23,12 +45,15 @@ class Blockchain {
block.hash !== Block.blockHash(block)) {
return false;
}
if (!Block.checkBlock(block)) {
return false;
}
}
return true;
}
//return false on failure, true on success
//return null on fail, returns the index of where they differ
replaceChain(newChain) {
if (newChain.length <= this.chain.length) {
console.log('Received chain is not longer than the current chain.');
@ -39,8 +64,18 @@ class Blockchain {
}
console.log('Replacing blockchain with the new chain.');
const oldChain = this.chain;
this.chain = newChain;
return true;
//find where they differ
for (let i = 1; i < oldChain.length; ++i) {
if (oldChain[i].hash !== newChain[i].hash) {
return i;
}
}
//if they didn't differ in the length of the old chain, must be one after
return oldChain.length;
}
}