QuadooScript Blockchain Example
The following script is based on the example presented in this video (which is the first of four in a series about blockchains using JavaScript). This example demonstrates these concepts:
- Generating random Elliptic Curve keys
- Signing and validating using Elliptic Curves
- Hashing using SHA-256
- Adding signed transactions to a pending transaction list
- Performing "work" to append new blocks of transactions to a blockchain
- Rewarding miners for cryptographically "mining" new blocks
#define PROV_RSA_FULL 1
namespace Crypto
{
var Module = Host.LoadQuadoo("QSCrypto.dll");
var Elliptic = Module.CreateElliptic();
var Context = Module.CreateContext(null, null, PROV_RSA_FULL, 0);
function CreateECKey ()
{
for(;;)
{
var oRandom = Context.GenRandom(32);
if(Elliptic.VerifyKey(oRandom))
return Elliptic.CreateKey(oRandom);
}
}
}
function CreateTransaction (oFromKey, strToAddress, nAmount)
{
var oTransaction = JSONCreateObject();
oTransaction.to = strToAddress;
oTransaction.amt = nAmount;
if(oFromKey)
{
oTransaction.from = oFromKey.Public.ToHex();
oTransaction.signature = oFromKey.Sign(TransactionDigest(oTransaction)).ToDER().ToHex();
}
else
oTransaction.from = null;
return oTransaction;
}
function TransactionDigest (oTransaction)
{
return Crypto.Module.SHA256(oTransaction.from + oTransaction.to + oTransaction.amt).GetDigest();
}
function ValidateTransaction (oTransaction)
{
var strFromAddress = oTransaction.from;
if(null == strFromAddress)
return true;
var oPublic = Crypto.Elliptic.KeyFromPublic(oTransaction.from);
return oPublic.Verify(TransactionDigest(oTransaction), oTransaction.signature);
}
class Block
{
property var TimeStamp, Transactions, PrevHash;
var m_strHash, m_nonce;
Block (oTimeStamp, aTransactions) :
TimeStamp(oTimeStamp),
Transactions(aTransactions),
m_nonce(0)
{
PrevHash = "";
m_strHash = Rehash();
}
property Hash
{
get ()
{
return m_strHash;
}
}
function Rehash ()
{
return Crypto.Module.SHA256(PrevHash + TimeStamp.ToISO8601() + (string)Transactions + m_nonce).GetHexKey();
}
function Mine (nDifficulty)
{
var strZero = (string)stringbuilder().AppendChar('0', nDifficulty);
while(strcmpn(m_strHash, strZero))
{
m_nonce++;
m_strHash = Rehash();
}
println("Mined: " + m_strHash + ", Nonce: " + m_nonce);
}
function Validate ()
{
var cTransactions = len(Transactions);
for(int i = 0; i < cTransactions; i++)
{
if(!ValidateTransaction(Transactions[i]))
return false;
}
return true;
}
};
class Blockchain
{
var m_aChain[];
var m_aPending = JSONCreateArray();
property var Difficulty = 3;
property var MiningReward = 10;
Blockchain ()
{
m_aChain.Append(CreateRootBlock());
}
function CreateRootBlock ()
{
return new Block(Host.ParseDate("1/1/2000"), null);
}
function GetLatestBlock ()
{
return m_aChain[len(m_aChain) - 1];
}
function MinePending (strRewardAddress)
{
var oBlock = new Block(nowutc(), m_aPending);
oBlock.PrevHash = GetLatestBlock().Hash;
oBlock.Mine(Difficulty);
m_aChain.Append(oBlock);
m_aPending = JSONCreateArray();
m_aPending.Append(CreateTransaction(null, strRewardAddress, MiningReward));
}
function AddTransaction (oTransaction)
{
if(null == oTransaction.from || null == oTransaction.to)
return false;
if(ValidateTransaction(oTransaction))
{
m_aPending.Append(oTransaction);
return true;
}
return false;
}
function GetBalance (strAddress)
{
int nBalance;
int cBlocks = len(m_aChain);
// The root block can always be skipped.
for(int nBlock = 1; nBlock < cBlocks; nBlock++)
{
var oBlock = m_aChain[nBlock];
var aTransactions = oBlock.Transactions;
var cTransactions = len(aTransactions);
for(int nTrans = 0; nTrans < cTransactions; nTrans++)
{
var oTrans = aTransactions[nTrans];
if(oTrans.from == strAddress)
nBalance -= oTrans.amt;
if(oTrans.to == strAddress)
nBalance += oTrans.amt;
}
}
return nBalance;
}
function Validate ()
{
var oPrev = m_aChain[0];
for(int i = 1; i < len(m_aChain); i++)
{
var oBlock = m_aChain[i];
if(oBlock.Hash != oBlock.Rehash())
return false;
if(oPrev.Hash != oBlock.PrevHash)
return false;
if(!oBlock.Validate())
return false;
oPrev = oBlock;
}
return true;
}
};
function main ()
{
var oKey1 = Crypto.CreateECKey();
var oKey2 = Crypto.CreateECKey();
var oKey3 = Crypto.CreateECKey();
var oBlockchain = new Blockchain;
println("Valid: " + oBlockchain.Validate());
oBlockchain.AddTransaction(CreateTransaction(oKey1, oKey2.Public.ToHex(), 100));
oBlockchain.MinePending(oKey3.Public.ToHex());
println("Valid: " + oBlockchain.Validate());
oBlockchain.AddTransaction(CreateTransaction(oKey2, oKey1.Public.ToHex(), 100));
oBlockchain.MinePending(oKey3.Public.ToHex());
println("Valid: " + oBlockchain.Validate());
println("Balance for " + oKey3.Public.ToHex() + ": " + oBlockchain.GetBalance(oKey3.Public.ToHex()));
}