Packages
💰Customisable Bitcoin-Blockchain ecosystem in Elixir. Includes blockchain, crypto wallets, miners and simulation of transactions.
Current section
Files
Jump to
Current section
Files
lib/bitcoin.ex
defmodule Bitcoin do
# Blockchain, wallets and miners are initialized from here and call to transaction automator is made
def start(users, noMiners, noTransactions, initialBalance) do
{:ok, blockChainPID} = BlockChainGenServer.start_link()
Registry.start_link(keys: :unique, name: :node_store)
verificationAccumulatorPID = spawn(Bitcoin, :verificationAccumulator, [0, users, blockChainPID, blockChainPID, [], []])
minersList = Enum.reduce(1..noMiners, [], fn _, acc ->
pid = spawn(Miner, :miningNode, [blockChainPID, verificationAccumulatorPID, 0])
acc ++ [pid]
end)
transactionAutomatorPID = spawn(Bitcoin, :transactionAutomator, [users, noTransactions, 0, self(), blockChainPID])
for i <- 1..users do
{:ok, {priv, pub}} = RsaEx.generate_keypair("512")
WalletGenServer.start_link(i, priv, pub, initialBalance, minersList, transactionAutomatorPID)
end
send transactionAutomatorPID, {:startTransaction}
receive do
{:allTransactionsDone, blockChain} -> {:allTransactionsDone, blockChain}
end
end
# Chooses random pairs of users and initiates transaction between them. Also captures result when all transaction have been completed.
def transactionAutomator(users, noTransactions, counter, caller, blockChainPID) do
receive do
{:startTransaction} ->
cond do
(counter + 1) <= noTransactions ->
pair = Enum.to_list(Helpers.pairs(2, users, MapSet.new()))
IO.puts("\nTransaction initiated between: 👜#{inspect(Enum.at(pair, 0))} ------> 👜#{inspect(Enum.at(pair, 1))}")
IO.puts("testing update")
sendersPID = WalletGenServer.pidRetriever(Enum.at(pair, 0))
receiversPID = WalletGenServer.pidRetriever(Enum.at(pair, 1))
receiversWallet = GenServer.call(receiversPID, {:getWallet})
receiversPK = Map.get(receiversWallet,"publicKey")
GenServer.call(sendersPID, {:initiateTransaction, [sendersPID, receiversPK]})
transactionAutomator(users, noTransactions, counter + 1, caller, blockChainPID)
true ->
IO.puts("\nAll transactions successfully completed.\n")
finalBalances = Enum.reduce(1..users, %{}, fn i, acc ->
walletPID = WalletGenServer.pidRetriever(i)
wallet = GenServer.call(walletPID, {:getWallet})
balance = Map.get(wallet, "btc")
Map.put(acc, :"wallet no #{inspect(i)}", balance)
end)
IO.puts("FINAL BALANCES: #{inspect(finalBalances)}")
send caller, {:allTransactionsDone, GenServer.call(blockChainPID, {:getBlockChain})}
end
{:transactionEnded} ->
send self(), {:startTransaction}
transactionAutomator(users, noTransactions, counter, caller, blockChainPID)
end
end
# Collects approval from all user in the ecosystem for a new block added to the blockchain and informs everyone if a fraudulent block was added
def verificationAccumulator(receiveCounter, counterLimit, blockChainPID, senderPID, minersList, decisionsList) do
receive do
{:ok, senderPID, minersList} ->
for i<-1..Registry.count(:node_store) do
walletPID = WalletGenServer.pidRetriever(i)
GenServer.call(walletPID, {:verifyBlockChain, [self(), blockChainPID]})
end
Bitcoin.verificationAccumulator(0, counterLimit, blockChainPID, senderPID, minersList, [])
{:ok, receiveDecision} ->
cond do
receiveCounter + 1 < counterLimit ->
decisionsList = decisionsList ++ [receiveDecision]
Bitcoin.verificationAccumulator(receiveCounter + 1, counterLimit, blockChainPID, senderPID, minersList, decisionsList)
receiveCounter + 1 == counterLimit ->
fraud = Enum.any?(decisionsList, fn x -> x == false end)
blockChain = GenServer.call(blockChainPID, {:getBlockChain})
if(fraud) do
for i <- 0..(length(minersList) - 1) do
send Enum.at(minersList, i), {:decrementPointer}
end
List.delete_at(blockChain, length(blockChain) - 1)
Bitcoin.verificationAccumulator(0, counterLimit, blockChainPID, senderPID, minersList, [])
else
IO.puts("✅ New block verified by all users")
latestBlock = Enum.at(blockChain, length(blockChain) - 1)
transactionData = Map.get(latestBlock, :transactionData)
receiversPK = Map.get(transactionData, "receiversPK")
btc = Map.get(transactionData, "btc")
GenServer.cast(senderPID, {:decrementBTC, btc})
for i <- 1..Registry.count(:node_store) do
walletPID = WalletGenServer.pidRetriever(i)
GenServer.cast(walletPID, {:checkTransactionReceiver, btc, receiversPK})
end
Bitcoin.verificationAccumulator(0, counterLimit, blockChainPID, senderPID, minersList, [])
end
end
end
end
end