Creating a simple cryptocurrency: part 3
Accounts, balances and transactions
A scryp account has an id and a balance.
The id is the 32-byte public key of the account holder.
The balance is a 4-byte integer which can have a value from 0 to 232-1; the combined balance of all accounts is 232-1 or 4,294,967,295 ~ 4.3 billion. The account unit of balance is the scryp.
The goal of scryp servers is to keep an accurate and current record of all account balances. Together, an account id and balance form a 36-byte data set:
id | balance |
---|---|
32 bytes | 4 bytes |
A transaction consists of a 140-byte data set with the following fields, in order:
time stamp | amount | payee id | payer id | payer signature |
---|---|---|---|---|
8 bytes | 4 bytes | 32 bytes | 32 bytes | 64 bytes |
The time stamp is the number of milliseconds elapsed since 00:00:00 01-01-1970 UTC, not including leap seconds; it has the same value for everyone on Earth. See the current Unix time stamp in decimal form here.
The amount is the payment amount.
The payee id is the destination account id.
The payer id is the origination account id.
The payer signature is the payer's signature of the preceding 76 bytes using the payer's public/secret key pair.
These are the basic data structures of scryp. It's doubtful that a cryptocurrency can be any simpler.
Here's a node.js program that packs a transaction, signs it, verifies it, and unpacks one field:
ed = require('ed25519-supercop')
function padZero (str, len) { // pad beginning of string with zeros if length is less than len
if (len <= str.length) return str
return '0'.repeat(len - str.length) + str
}
timestamp = padZero(Date.now().toString(16), 16) // 16 hex-digit string
amount = 100 // decimal
amt = padZero(parseInt(amount, 10).toString(16), 8) // hex-digit string '00000064'
payeeId = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' // 32-byte payee's public key
payerId = '709efff9bb2e3af305070b275fe9defa1f0239910a352cfedc76ad96263a0916' // 32-byte payer's public key
payerSecretKey = '687ad3ca223beacc9d59ffb4451acd0e8b4fa67583b672997433d5a16671ba78cb51a103c437f8cf1dae192a42b361413a0e317e535147484ade8b65ed6f0596' // 64-byte payer's secret key
rawTx = Buffer.from(timestamp + amt + payeeId + payerId, 'hex')
signedTx = Buffer.concat([rawTx, ed.sign(rawTx, payerId, payerSecretKey)])
console.log(signedTx.length) // 140 bytes
console.log(signedTx)
console.log(signedTx.toString('hex'))
console.log(ed.verify(signedTx.slice(76), signedTx.slice(0, 76), signedTx.slice(44, 76))) // true
console.log(parseInt(signedTx.slice(8, 12).toString('hex'), 16)) // amount = 100
The signed transaction in hex form is:
0000015c989bfe98000000640123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef709efff9bb2e3af305070b275fe9defa1f0239910a352cfedc76ad96263a0916c7d000457c0f447b1059a142d681e65a71dc96495c289cef3068d70dfc4c03443d823219dbf8d39471c4337cf664ca6f3562871c124f4d6877bfd794de504501