Created
January 4, 2018 21:25
-
-
Save azamsharp/be552f1d50b988533ba56264367ddbf9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: Playground - noun: a place where people can play | |
import Cocoa | |
import Foundation | |
struct BrokenRule : Codable { | |
var contractName :String | |
var message :String | |
} | |
protocol SmartContract { | |
func apply(to transaction :Transaction) | |
} | |
class MaximumAmountTransferFundsContract : SmartContract { | |
func apply(to transaction: Transaction) { | |
if transaction.amount > 5000 { | |
transaction.addBrokenRule(BrokenRule(contractName: "MaximumAmountTransferFundsContract", message: "Your amount is more than $5000")) | |
} | |
} | |
} | |
class InternationalDomesticTransferFeesContract : SmartContract { | |
func apply(to transaction: Transaction) { | |
switch transaction.transactionType { | |
case .international: | |
transaction.fees = 10 | |
case .domestic: | |
transaction.fees = 2 | |
} | |
if transaction.amount < transaction.fees { | |
print("broken rule") | |
transaction.addBrokenRule(BrokenRule(contractName: "InternationalDomesticTransferFeesContract", message: "Insufficient funds")) | |
} else { | |
transaction.amount -= transaction.fees | |
} | |
} | |
} | |
enum TranslationType : String, Codable { | |
case international | |
case domestic | |
} | |
class Transaction : Codable { | |
var from :String | |
var to :String | |
var amount :Double | |
var fees :Double = 0.0 | |
var transactionType :TranslationType = .domestic | |
var brokenRules :[BrokenRule] = [BrokenRule]() | |
var isValid :Bool { | |
get { | |
return self.brokenRules.count == 0 | |
} | |
} | |
init(from :String, to:String, amount :Double, transactionType :TranslationType = .domestic) { | |
self.from = from | |
self.to = to | |
self.amount = amount | |
self.transactionType = transactionType | |
} | |
func addBrokenRule(_ brokenRule :BrokenRule) { | |
self.brokenRules.append(brokenRule) | |
} | |
} | |
class Block { | |
var index :Int = 0 | |
var dateCreated :String | |
var previousHash :String = "" | |
var hash :String! | |
var nonce :Int | |
var key :String { | |
get { | |
let transactionData = try! JSONEncoder().encode(self.transactions) | |
let transactionsJSONString = String(data: transactionData, encoding: .utf8) | |
let keyPart = String(self.index) + self.dateCreated + self.previousHash + String(self.nonce) | |
return (keyPart + transactionsJSONString!) | |
} | |
} | |
private (set) var transactions :[Transaction] = [Transaction]() | |
func addTransaction(transaction :Transaction, contracts :[SmartContract] = [SmartContract]()) { | |
// run the smart contracts | |
contracts.forEach { contract in | |
contract.apply(to: transaction) | |
} | |
if transaction.isValid { | |
self.transactions.append(transaction) | |
} | |
} | |
init() { | |
self.dateCreated = Date().toString() | |
self.nonce = 0 | |
} | |
} | |
class Blockchain { | |
private (set) var blocks :[Block] = [Block]() | |
private (set) var contracts :[SmartContract] = [SmartContract]() | |
init() { | |
// initialize contracts | |
initializeContracts() | |
} | |
private func initializeContracts() { | |
self.contracts = [InternationalDomesticTransferFeesContract(),MaximumAmountTransferFundsContract()] | |
} | |
func addBlock(_ block :Block) { | |
if block.transactions.count > 0 { | |
self.blocks.append(block) | |
} | |
} | |
// this function is used to create a next block | |
func generateNextBlock() -> Block { | |
let block = Block() | |
if self.blocks.isEmpty { | |
block.previousHash = "0000000000000000" | |
block.hash = generateHash(for :block) | |
} | |
else { | |
let previousBlock = getPreviousBlock() | |
block.previousHash = previousBlock.hash | |
block.index = self.blocks.count | |
block.hash = generateHash(for: block) | |
} | |
return block | |
} | |
private func getPreviousBlock() -> Block { | |
return self.blocks[self.blocks.count - 1] | |
} | |
func displayAllBlocks() { | |
for block in self.blocks { | |
print("------ Block \(block.index) ---------") | |
print("Date Created : \(block.dateCreated) ") | |
print("Nonce : \(block.nonce) ") | |
print("Previous Hash : \(block.previousHash) ") | |
print("Hash : \(block.hash!) ") | |
print("------Transactions--------") | |
block.transactions.forEach { transaction in | |
print("Transaction from \(transaction.from)") | |
print("Transaction to \(transaction.to)") | |
print("Transaction amount \(transaction.amount)") | |
print("Transaction Fees \(transaction.fees) ") | |
print("Transaction Type \(transaction.transactionType.rawValue) ") | |
} | |
} | |
} | |
func generateHash(for block:Block) -> String { | |
var hash = block.key.sha1Hash() | |
while(!hash.hasPrefix("00")) { | |
block.nonce += 1 | |
hash = block.key.sha1Hash() | |
print(hash) | |
} | |
return hash | |
} | |
} | |
// String Extension | |
extension String { | |
func sha1Hash() -> String { | |
let task = Process() | |
task.launchPath = "/usr/bin/shasum" | |
task.arguments = [] | |
let inputPipe = Pipe() | |
inputPipe.fileHandleForWriting.write(self.data(using: String.Encoding.utf8)!) | |
inputPipe.fileHandleForWriting.closeFile() | |
let outputPipe = Pipe() | |
task.standardOutput = outputPipe | |
task.standardInput = inputPipe | |
task.launch() | |
let data = outputPipe.fileHandleForReading.readDataToEndOfFile() | |
let hash = String(data: data, encoding: String.Encoding.utf8)! | |
return hash.replacingOccurrences(of: " -\n", with: "") | |
} | |
} | |
extension Date { | |
func toString() -> String { | |
let formatter = DateFormatter() | |
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" | |
return formatter.string(from: self) | |
} | |
} | |
let transaction = Transaction(from: "Azam", to: "Mary", amount: 56, transactionType : .international) | |
let blockchain = Blockchain() | |
let block = blockchain.generateNextBlock() | |
block.addTransaction(transaction: transaction, contracts: blockchain.contracts) | |
if !transaction.isValid { | |
transaction.brokenRules.forEach { brokenRule in | |
print("BrokenRule Name - \(brokenRule.contractName) ") | |
print("BrokenRule Message - \(brokenRule.message) ") | |
} | |
} else { | |
blockchain.addBlock(block) | |
blockchain.displayAllBlocks() | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment