From 838d064299354effb796b151fe1d7f2bf6ea86f4 Mon Sep 17 00:00:00 2001 From: fluency03 Date: Tue, 8 May 2018 22:12:29 +0200 Subject: [PATCH] rename SingleWallet to KeyContainer --- README.md | 2 + .../api/actors/BlockchainActor.scala | 20 ++++++--- .../blockchain/api/actors/PeerActor.scala | 16 +++---- .../fluency03/blockchain/api/package.scala | 7 --- .../api/routes/BlockPoolRoutes.scala | 2 +- .../api/routes/BlockchainRoutes.scala | 15 ++++++- ...{SingleWallet.scala => KeyContainer.scala} | 14 +++--- .../fluency03/blockchain/core/Wallet.scala | 41 ++++++++++++++++++ .../blockchain/crypto/Secp256k1.scala | 14 ++++++ .../api/actors/BlockchainActorTest.scala | 4 +- .../blockchain/core/KeyContainerTest.scala | 43 +++++++++++++++++++ .../blockchain/core/SingleWalletTest.scala | 43 ------------------- .../blockchain/core/WalletTest.scala | 11 +++++ 13 files changed, 158 insertions(+), 74 deletions(-) rename src/main/scala/com/github/fluency03/blockchain/core/{SingleWallet.scala => KeyContainer.scala} (68%) create mode 100644 src/main/scala/com/github/fluency03/blockchain/core/Wallet.scala create mode 100644 src/test/scala/com/github/fluency03/blockchain/core/KeyContainerTest.scala delete mode 100644 src/test/scala/com/github/fluency03/blockchain/core/SingleWalletTest.scala create mode 100644 src/test/scala/com/github/fluency03/blockchain/core/WalletTest.scala diff --git a/README.md b/README.md index 31e6832..79452b8 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ Please see the full API Documentation on Postman: [blockchain-in-scala](https:// ### Summary: +Default hostname and port is `localhost:9090`. + #### root ``` diff --git a/src/main/scala/com/github/fluency03/blockchain/api/actors/BlockchainActor.scala b/src/main/scala/com/github/fluency03/blockchain/api/actors/BlockchainActor.scala index 7272050..0cb0b0a 100644 --- a/src/main/scala/com/github/fluency03/blockchain/api/actors/BlockchainActor.scala +++ b/src/main/scala/com/github/fluency03/blockchain/api/actors/BlockchainActor.scala @@ -19,7 +19,8 @@ object BlockchainActor { final case object DeleteBlockchain extends BlockchainMsg final case object CheckBlockchainValidity extends BlockchainMsg - final case class GetBlockFromChain(hash: String) extends BlockMsg + final case class GetBlockByHash(hash: String) extends BlockMsg + final case class GetBlocksByHashesAndIndices(hashes: Set[String], indices: Set[Int]) extends BlockMsg final case object GetLastBlock extends BlockMsg final case class GetTxOfBlock(id: String, hash: String) extends BlockMsg final case class AppendBlock(block: Block) extends BlockMsg @@ -59,7 +60,8 @@ class BlockchainActor extends ActorSupport { } private def inCaseOfBlockMsg(msg: BlockMsg): Unit = msg match { - case GetBlockFromChain(hash) => onGetBlockFromChain(hash) + case GetBlockByHash(hash) => onGetBlockByHash(hash) + case GetBlocksByHashesAndIndices(hashes, indices) => onGetBlocksByHashesAndIndices(hashes, indices) case GetLastBlock => onGetLastBlock() case GetTxOfBlock(id, hash) => onGetTxOfBlock(id, hash) case AppendBlock(block) => onAppendBlock(block) @@ -108,7 +110,10 @@ class BlockchainActor extends ActorSupport { * Handlers for each of the BlockMsg. */ - private def onGetBlockFromChain(hash: String): Unit = sender() ! getBlockFromChain(hash) + private def onGetBlockByHash(hash: String): Unit = sender() ! getBlockByHash(hash) + + private def onGetBlocksByHashesAndIndices(hashes: Set[String], indices: Set[Int]): Unit = + sender() ! getBlocksByHashesAndIndices(hashes, indices) private def onGetLastBlock(): Unit = blockchainOpt match { case Some(blockchain) => sender() ! blockchain.lastBlock() @@ -118,7 +123,7 @@ class BlockchainActor extends ActorSupport { sender() ! None } - private def onGetTxOfBlock(id: String, hash: String): Unit = getBlockFromChain(hash) match { + private def onGetTxOfBlock(id: String, hash: String): Unit = getBlockByHash(hash) match { case Some(block) => sender() ! block.transactions.find(_.id == id) case None => sender() ! None } @@ -188,7 +193,7 @@ class BlockchainActor extends ActorSupport { private def onGetBlockFromPool(hash: String): Unit = blockPoolActor forward BlockPoolActor.GetBlock(hash) - private def getBlockFromChain(hash: String): Option[Block] = hashIndexMapping.get(hash) match { + private def getBlockByHash(hash: String): Option[Block] = hashIndexMapping.get(hash) match { case Some(index) => blockchainOpt match { case Some(blockchain) => Some(blockchain.chain(index)) case None => @@ -199,4 +204,9 @@ class BlockchainActor extends ActorSupport { case None => None } + private def getBlocksByHashesAndIndices(hashes: Set[String], indices: Set[Int]): Set[Block] = { + ??? + } + + } diff --git a/src/main/scala/com/github/fluency03/blockchain/api/actors/PeerActor.scala b/src/main/scala/com/github/fluency03/blockchain/api/actors/PeerActor.scala index dbbe562..8a691a5 100644 --- a/src/main/scala/com/github/fluency03/blockchain/api/actors/PeerActor.scala +++ b/src/main/scala/com/github/fluency03/blockchain/api/actors/PeerActor.scala @@ -1,11 +1,9 @@ package com.github.fluency03.blockchain package api.actors -import java.security.KeyPair - import akka.actor.Props import com.github.fluency03.blockchain.api.actors.PeerActor._ -import com.github.fluency03.blockchain.core.{Peer, SingleWallet} +import com.github.fluency03.blockchain.core.{Peer, KeyContainer} import scala.collection.mutable @@ -19,12 +17,12 @@ class PeerActor extends ActorSupport { override def preStart(): Unit = { log.info("{} started!", this.getClass.getSimpleName) addWallet() - log.info("Created initial wallet: {}", wallets.head._1) + log.info("Created initial wallet: {}", wallet.head._1) } override def postStop(): Unit = log.info("{} stopped!", this.getClass.getSimpleName) // TODO (Chang): need persistence - val wallets = mutable.Map.empty[String, SingleWallet] + val wallet = mutable.Map.empty[String, KeyContainer] val others = mutable.Map.empty[String, Peer] /** @@ -35,15 +33,15 @@ class PeerActor extends ActorSupport { */ def receive: Receive = { - case GetPublicKeys => sender() ! wallets.values.map(_.address).toSet + case GetPublicKeys => sender() ! wallet.values.map(_.address).toSet case CreateWallet => sender() ! addWallet() case _ => unhandled _ } private def addWallet(): String = { - val newWallet = SingleWallet() - wallets += (newWallet.address -> newWallet) - newWallet.address + val kc = KeyContainer() + wallet += (kc.address -> kc) + kc.address } } diff --git a/src/main/scala/com/github/fluency03/blockchain/api/package.scala b/src/main/scala/com/github/fluency03/blockchain/api/package.scala index 8c0cc3d..bc52e1b 100644 --- a/src/main/scala/com/github/fluency03/blockchain/api/package.scala +++ b/src/main/scala/com/github/fluency03/blockchain/api/package.scala @@ -16,11 +16,4 @@ package object api { val PARENT_UP = "../" - - - - - - - } diff --git a/src/main/scala/com/github/fluency03/blockchain/api/routes/BlockPoolRoutes.scala b/src/main/scala/com/github/fluency03/blockchain/api/routes/BlockPoolRoutes.scala index cfb3faf..c4fe582 100644 --- a/src/main/scala/com/github/fluency03/blockchain/api/routes/BlockPoolRoutes.scala +++ b/src/main/scala/com/github/fluency03/blockchain/api/routes/BlockPoolRoutes.scala @@ -32,7 +32,7 @@ trait BlockPoolRoutes extends RoutesSupport { } ~ path(NEXT_BLOCK) { post { - parameters('id.as(CsvSeq[String]).?) { idsOpt: Option[Seq[String]] => + parameters('ids.as(CsvSeq[String]).?) { idsOpt: Option[Seq[String]] => entity(as[Input]) { in => val maybeNextBlock: Future[Option[Block]] = (blockPoolActor ? MineAndAddNextBlock(in.content, idsOpt.getOrElse(Seq.empty[String]))).mapTo[Option[Block]] diff --git a/src/main/scala/com/github/fluency03/blockchain/api/routes/BlockchainRoutes.scala b/src/main/scala/com/github/fluency03/blockchain/api/routes/BlockchainRoutes.scala index b85e05c..60109f5 100644 --- a/src/main/scala/com/github/fluency03/blockchain/api/routes/BlockchainRoutes.scala +++ b/src/main/scala/com/github/fluency03/blockchain/api/routes/BlockchainRoutes.scala @@ -73,10 +73,23 @@ trait BlockchainRoutes extends RoutesSupport { onSuccess(blockchainDeleted) { respondOnDeletion } } } ~ + path(BLOCKS) { + get { + parameters('hashes.as(CsvSeq[String]).?, 'indices.as(CsvSeq[Int]).?) { + (hashesOpt: Option[Seq[String]], indicesOpt: Option[Seq[Int]]) => + val blocks: Future[Set[Block]] = (blockchainActor ? + GetBlocksByHashesAndIndices( + hashesOpt.getOrElse(Seq.empty[String]).toSet, + indicesOpt.getOrElse(Seq.empty[Int]).toSet)) + .mapTo[Set[Block]] + rejectEmptyResponse { complete(blocks) } + } + } + } ~ pathPrefix(BLOCK / Segment) { hash => pathEnd { get { - val maybeBlock: Future[Option[Block]] = (blockchainActor ? GetBlockFromChain(hash)).mapTo[Option[Block]] + val maybeBlock: Future[Option[Block]] = (blockchainActor ? GetBlockByHash(hash)).mapTo[Option[Block]] rejectEmptyResponse { complete(maybeBlock) } } } ~ diff --git a/src/main/scala/com/github/fluency03/blockchain/core/SingleWallet.scala b/src/main/scala/com/github/fluency03/blockchain/core/KeyContainer.scala similarity index 68% rename from src/main/scala/com/github/fluency03/blockchain/core/SingleWallet.scala rename to src/main/scala/com/github/fluency03/blockchain/core/KeyContainer.scala index 1348919..3fd74c5 100644 --- a/src/main/scala/com/github/fluency03/blockchain/core/SingleWallet.scala +++ b/src/main/scala/com/github/fluency03/blockchain/core/KeyContainer.scala @@ -3,29 +3,31 @@ package core import java.security.KeyPair -import com.github.fluency03.blockchain.core.SingleWallet.balanceOfWallet +import com.github.fluency03.blockchain.core.KeyContainer.balanceOfKey import com.github.fluency03.blockchain.core.Transaction.signTxIn import com.github.fluency03.blockchain.crypto.Secp256k1 import scala.collection.mutable -case class SingleWallet() { +case class KeyContainer() { private[this] val keyPair: KeyPair = Secp256k1.generateKeyPair() lazy val address: String = keyPair.getPublic.toHex - def balance(uTxOs: mutable.Map[Outpoint, TxOut]): Long = balanceOfWallet(this, uTxOs) + lazy val publicKeyHex: String = keyPair.getPublic.toHex + + def balance(uTxOs: mutable.Map[Outpoint, TxOut]): Long = balanceOfKey(this, uTxOs) def sign(txId: String, txIn: TxIn, uTxOs: mutable.Map[Outpoint, TxOut]): Option[TxIn] = signTxIn(txId, txIn, keyPair, uTxOs) } -object SingleWallet { +object KeyContainer { - def balanceOfWallet(wallet: SingleWallet, uTxOs: mutable.Map[Outpoint, TxOut]): Long = - balanceOfAddress(wallet.address, uTxOs) + def balanceOfKey(kc: KeyContainer, uTxOs: mutable.Map[Outpoint, TxOut]): Long = + balanceOfAddress(kc.address, uTxOs) def balanceOfAddress(address: String, uTxOs: mutable.Map[Outpoint, TxOut]): Long = uTxOs.values.filter(_.address == address).map(_.amount).sum diff --git a/src/main/scala/com/github/fluency03/blockchain/core/Wallet.scala b/src/main/scala/com/github/fluency03/blockchain/core/Wallet.scala new file mode 100644 index 0000000..4546f5e --- /dev/null +++ b/src/main/scala/com/github/fluency03/blockchain/core/Wallet.scala @@ -0,0 +1,41 @@ +package com.github.fluency03.blockchain.core + +trait Wallet { + +} + + + +case class RandomWallet() extends Wallet { + + + +} + + + +object RandomWallet { + + + +} + + + + +case class SeededWallet() extends Wallet { + + + +} + + + +object SeededWallet { + + + +} + + + diff --git a/src/main/scala/com/github/fluency03/blockchain/crypto/Secp256k1.scala b/src/main/scala/com/github/fluency03/blockchain/crypto/Secp256k1.scala index 3b89328..140169f 100644 --- a/src/main/scala/com/github/fluency03/blockchain/crypto/Secp256k1.scala +++ b/src/main/scala/com/github/fluency03/blockchain/crypto/Secp256k1.scala @@ -74,7 +74,21 @@ object Secp256k1 { def publicKeyHexToAddress(publicKey: String, networkBytes: String = "00"): String = Base58.checkEncode(networkBytes.hex2Bytes ++ publicKey.hex2Bytes.toHash160Digest) + /** + * Convert a Public Key to its Base58 Address. + * 0 - Private ECDSA Key + * 1 - Public ECDSA Key + * 2 - SHA-256 hash of 1 + * 3 - RIPEMD-160 Hash of 2 + * 4 - Adding network bytes to 3 + * 5 - SHA-256 hash of 4 + * 6 - SHA-256 hash of 5 + * 7 - First four bytes of 6 + * 8 - Adding 7 at the end of 4 + * 9 - Base58 encoding of 8 + */ def publicKeyToAddress(publicKey: PublicKey, networkBytes: String = "00"): String = Base58.checkEncode(networkBytes.hex2Bytes ++ publicKeyToBytes(publicKey).toHash160Digest) + } diff --git a/src/test/scala/com/github/fluency03/blockchain/api/actors/BlockchainActorTest.scala b/src/test/scala/com/github/fluency03/blockchain/api/actors/BlockchainActorTest.scala index 57d83ad..35eaf48 100644 --- a/src/test/scala/com/github/fluency03/blockchain/api/actors/BlockchainActorTest.scala +++ b/src/test/scala/com/github/fluency03/blockchain/api/actors/BlockchainActorTest.scala @@ -42,11 +42,11 @@ class BlockchainActorTest extends TestKit(ActorSystem("BlockchainActorTest")) wi blockchainActor ! GetBlockchain val blockchain = expectMsgType[Some[Blockchain]].get - blockchainActor ! GetBlockFromChain("somehash") + blockchainActor ! GetBlockByHash("somehash") expectMsg(None) val genesis = Block.genesisBlock - blockchainActor ! GetBlockFromChain(genesis.hash) + blockchainActor ! GetBlockByHash(genesis.hash) expectMsg(Some(genesis)) blockchainActor ! GetLastBlock diff --git a/src/test/scala/com/github/fluency03/blockchain/core/KeyContainerTest.scala b/src/test/scala/com/github/fluency03/blockchain/core/KeyContainerTest.scala new file mode 100644 index 0000000..e28624b --- /dev/null +++ b/src/test/scala/com/github/fluency03/blockchain/core/KeyContainerTest.scala @@ -0,0 +1,43 @@ +package com.github.fluency03.blockchain +package core + +import com.github.fluency03.blockchain.core.KeyContainer._ + +import org.scalatest.{FlatSpec, Matchers} + +import scala.collection.mutable + +class KeyContainerTest extends FlatSpec with Matchers { + + "balanceOfKey" should "obtain the balance of a Key based on UTXOs." in { + val kc = KeyContainer() + val uTxOs: mutable.Map[Outpoint, TxOut] = mutable.Map.empty[Outpoint, TxOut] + + balanceOfKey(kc, uTxOs) shouldEqual 0 + kc.balance(uTxOs) shouldEqual 0 + + uTxOs += (Outpoint("def0", 0) -> TxOut(kc.address, 40)) + uTxOs += (Outpoint("def0", 1) -> TxOut("abc4", 40)) + + balanceOfKey(kc, uTxOs) shouldEqual 40 + kc.balance(uTxOs) shouldEqual 40 + } + + "KeyContainer" should "be able to sign a TxIn." in { + val kc = KeyContainer() + val id = "".toSha256 + val txIn = TxIn(Outpoint("def0", 0), "abc") + val uTxOs: mutable.Map[Outpoint, TxOut] = mutable.Map.empty[Outpoint, TxOut] + + val signedTxIn0 = kc.sign(id, txIn, uTxOs) + signedTxIn0 shouldEqual None + + uTxOs += (Outpoint("def0", 0) -> TxOut(kc.address, 40)) + uTxOs += (Outpoint("def0", 1) -> TxOut("abc4", 40)) + + val signedTxIn = kc.sign(id, txIn, uTxOs) + signedTxIn shouldEqual Some(TxIn(Outpoint("def0", 0), signedTxIn.get.signature)) + } + + +} diff --git a/src/test/scala/com/github/fluency03/blockchain/core/SingleWalletTest.scala b/src/test/scala/com/github/fluency03/blockchain/core/SingleWalletTest.scala deleted file mode 100644 index 3463d44..0000000 --- a/src/test/scala/com/github/fluency03/blockchain/core/SingleWalletTest.scala +++ /dev/null @@ -1,43 +0,0 @@ -package com.github.fluency03.blockchain -package core - -import com.github.fluency03.blockchain.core.SingleWallet._ - -import org.scalatest.{FlatSpec, Matchers} - -import scala.collection.mutable - -class SingleWalletTest extends FlatSpec with Matchers { - - "balanceOfWallet" should "obtain the balance of a Wallet based on UTXOs." in { - val wallet = SingleWallet() - val uTxOs: mutable.Map[Outpoint, TxOut] = mutable.Map.empty[Outpoint, TxOut] - - balanceOfWallet(wallet, uTxOs) shouldEqual 0 - wallet.balance(uTxOs) shouldEqual 0 - - uTxOs += (Outpoint("def0", 0) -> TxOut(wallet.address, 40)) - uTxOs += (Outpoint("def0", 1) -> TxOut("abc4", 40)) - - balanceOfWallet(wallet, uTxOs) shouldEqual 40 - wallet.balance(uTxOs) shouldEqual 40 - } - - "Wallet" should "be able to sign a TxIn." in { - val wallet = SingleWallet() - val id = "".toSha256 - val txIn = TxIn(Outpoint("def0", 0), "abc") - val uTxOs: mutable.Map[Outpoint, TxOut] = mutable.Map.empty[Outpoint, TxOut] - - val signedTxIn0 = wallet.sign(id, txIn, uTxOs) - signedTxIn0 shouldEqual None - - uTxOs += (Outpoint("def0", 0) -> TxOut(wallet.address, 40)) - uTxOs += (Outpoint("def0", 1) -> TxOut("abc4", 40)) - - val signedTxIn = wallet.sign(id, txIn, uTxOs) - signedTxIn shouldEqual Some(TxIn(Outpoint("def0", 0), signedTxIn.get.signature)) - } - - -} diff --git a/src/test/scala/com/github/fluency03/blockchain/core/WalletTest.scala b/src/test/scala/com/github/fluency03/blockchain/core/WalletTest.scala new file mode 100644 index 0000000..4d3c721 --- /dev/null +++ b/src/test/scala/com/github/fluency03/blockchain/core/WalletTest.scala @@ -0,0 +1,11 @@ +package com.github.fluency03.blockchain.core + +import org.scalatest.{FlatSpec, Matchers} + +class WalletTest extends FlatSpec with Matchers { + + "Wallet" should "do something." in { + + } + +}