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:

#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())); }