Skip to content

Commit

Permalink
add tests for Wallet and Merkle
Browse files Browse the repository at this point in the history
  • Loading branch information
fluency03 committed May 12, 2018
1 parent 7c62187 commit f997aaa
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.fluency03.blockchain
package core

import java.security.KeyPair
import java.security._

import com.github.fluency03.blockchain.core.KeyContainer.balanceOfKey
import com.github.fluency03.blockchain.core.Transaction.signTxIn
Expand All @@ -11,7 +11,7 @@ import scala.collection.mutable

case class KeyContainer() {

private[this] val keyPair: KeyPair = Secp256k1.generateKeyPair()
val keyPair: KeyPair = Secp256k1.generateKeyPair()

// TODO (Chang): change it to actual address (which is a Base58) of a PublicKey
lazy val address: HexString = keyPair.getPublic.toHex
Expand All @@ -20,6 +20,11 @@ case class KeyContainer() {

def balance(uTxOs: mutable.Map[Outpoint, TxOut]): Long = balanceOfKey(this, uTxOs)

def sign(data: Bytes): Bytes = Secp256k1.sign(data, keyPair.getPrivate)

def verify(data: Bytes, signature: Bytes): Boolean =
Secp256k1.verify(data, keyPair.getPublic, signature)

def sign(txId: String, txIn: TxIn, uTxOs: mutable.Map[Outpoint, TxOut]): Option[TxIn] =
signTxIn(txId, txIn, keyPair, uTxOs)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object Merkle {

@tailrec
def hashViaMerklePath(init: String, path: Seq[String], index: Int): String =
if (path.isEmpty) init
if (path.isEmpty) init.toLowerCase()
else {
val newHash = if (index % 2 == 0)
SHA256.hashAll(init, path.head)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.github.fluency03.blockchain.core
package com.github.fluency03.blockchain
package core

case class PeerSimple(name: String)
case class Peer(name: String, publicKeys: Set[String])
case class Peer(name: String, publicKeys: Set[HexString])
14 changes: 12 additions & 2 deletions src/main/scala/com/github/fluency03/blockchain/core/Wallet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import scala.collection.mutable

trait Wallet {

def size(): Int

def getKey(str: String): Option[KeyContainer]

def newKey(): KeyContainer
Expand All @@ -17,9 +19,14 @@ trait Wallet {

case class RandomWallet() extends Wallet {

private[this] val keys = mutable.Map.empty[String, KeyContainer]
val keys: mutable.Map[String, KeyContainer] = {
val initKeys = mutable.Map.empty[String, KeyContainer]
val aNewKey = KeyContainer()
initKeys += (aNewKey.publicKeyHex -> aNewKey)
initKeys
}

override def getKey(str: String): Option[KeyContainer] = keys.get(str)
override def getKey(hash: String): Option[KeyContainer] = keys.get(hash)

override def newKey(): KeyContainer = {
val aNewKey = KeyContainer()
Expand All @@ -30,6 +37,7 @@ case class RandomWallet() extends Wallet {
override def balance(uTxOs: mutable.Map[Outpoint, TxOut]): Long =
keys.values.map(k => balanceOfKey(k, uTxOs)).sum

override def size(): Int = keys.size

}

Expand All @@ -43,4 +51,6 @@ case class SeededWallet() extends Wallet {

override def balance(uTxOs: mutable.Map[Outpoint, TxOut]): Long = ???

override def size(): Int = ???

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ object Secp256k1 {
val keySpec: PKCS8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey)
val keyFactory: KeyFactory = KeyFactory.getInstance(KEY_ALGORITHM)
val key: PrivateKey = keyFactory.generatePrivate(keySpec)
sign(data, key)
}

def sign(data: Bytes, privateKey: PrivateKey): Bytes = {
val sig: Signature = Signature.getInstance(KEY_ALGORITHM, KEY_PROVIDER)
sig.initSign(key, new SecureRandom)
sig.initSign(privateKey, new SecureRandom)
sig.update(data)
sig.sign()
}
Expand All @@ -35,9 +38,12 @@ object Secp256k1 {
val keySpec: X509EncodedKeySpec = new X509EncodedKeySpec(publicKey)
val keyFactory: KeyFactory = KeyFactory.getInstance(KEY_ALGORITHM)
val key: PublicKey = keyFactory.generatePublic(keySpec)
verify(data, key, signature)
}

def verify(data: Bytes, publicKey: PublicKey, signature: Bytes): Boolean = {
val sig: Signature = Signature.getInstance(KEY_ALGORITHM, KEY_PROVIDER)
sig.initVerify(key)
sig.initVerify(publicKey)
sig.update(data)
sig.verify(signature)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class KeyContainerTest extends FlatSpec with Matchers {

val signedTxIn = kc.sign(id, txIn, uTxOs)
signedTxIn shouldEqual Some(TxIn(Outpoint("def0", 0), signedTxIn.get.signature))

kc.verify("abc".getBytes, kc.sign("abc".getBytes)) shouldEqual true
kc.verify("".getBytes, kc.sign("abc".getBytes)) shouldEqual false
kc.verify("123".getBytes, kc.sign("123".getBytes)) shouldEqual true
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class MerkleTest extends FlatSpec with Matchers {
computeRoot(List(t)) shouldEqual t.id
}

"A Merkle Tree" should "have a valid root hash." in {
it should "be able to compute valid root hash." in {
val h1 = "41ef4bb0b23661e66301aac36066912dac037827b4ae63a7b1165a5aa93ed4eb"
val h2 = "000031bee3fa033f2d69ae7d0d9f565bf3a235452ccf8a5edffb78cfbcdd7137"
val h12 = SHA256.hashAll(h1, h2)
Expand All @@ -41,4 +41,71 @@ class MerkleTest extends FlatSpec with Matchers {
computeRoot(List(t1, t2, t3)) shouldEqual SHA256.hashAll(th12, th33)
}

it should "be able to compute root hash via merkle path and verify it." in {
hashViaMerklePath(
"D97A21CF46FD5AFB0BF9EA4237BC4BF5C84E8B47D38D1EEE2BBEB5C0F8A1C625",
Seq.empty[HexString],
0) shouldEqual
"D97A21CF46FD5AFB0BF9EA4237BC4BF5C84E8B47D38D1EEE2BBEB5C0F8A1C625".toLowerCase()

hashViaMerklePath(
"D97A21CF46FD5AFB0BF9EA4237BC4BF5C84E8B47D38D1EEE2BBEB5C0F8A1C625",
Seq("AE1E670BDBF8AB984F412E6102C369AECA2CED933A1DE74712CCDA5EDAF4EE57"),
0) shouldEqual
"e2fbeaa16b00fb4ba139c62158971612aa8cddf6163082c74fa74ebb5004c10b"

hashViaMerklePath(
"D97A21CF46FD5AFB0BF9EA4237BC4BF5C84E8B47D38D1EEE2BBEB5C0F8A1C625",
Seq(
"AE1E670BDBF8AB984F412E6102C369AECA2CED933A1DE74712CCDA5EDAF4EE57",
"EFC2B3DB87FF4F00C79DFA8F732A23C0E18587A73A839B7710234583CDD03DB9"),
2) shouldEqual
"dce52948923f07840400d52cc5deb037c8cef400a2e97699146a291112477ce0"

hashViaMerklePath(
"e2fbeaa16b00fb4ba139c62158971612aa8cddf6163082c74fa74ebb5004c10b",
Seq("EFC2B3DB87FF4F00C79DFA8F732A23C0E18587A73A839B7710234583CDD03DB9"),
1) shouldEqual
"dce52948923f07840400d52cc5deb037c8cef400a2e97699146a291112477ce0"

hashViaMerklePath(
"D97A21CF46FD5AFB0BF9EA4237BC4BF5C84E8B47D38D1EEE2BBEB5C0F8A1C625",
Seq(
"AE1E670BDBF8AB984F412E6102C369AECA2CED933A1DE74712CCDA5EDAF4EE57",
"EFC2B3DB87FF4F00C79DFA8F732A23C0E18587A73A839B7710234583CDD03DB9",
"F1B6FE8FC2AB800E6D76EE975A002D3E67A60B51A62085A07289505B8D03F149"),
6) shouldEqual
hashViaMerklePath(
"dce52948923f07840400d52cc5deb037c8cef400a2e97699146a291112477ce0",
Seq("F1B6FE8FC2AB800E6D76EE975A002D3E67A60B51A62085A07289505B8D03F149"),
1)

hashViaMerklePath(
"D97A21CF46FD5AFB0BF9EA4237BC4BF5C84E8B47D38D1EEE2BBEB5C0F8A1C625",
Seq(
"AE1E670BDBF8AB984F412E6102C369AECA2CED933A1DE74712CCDA5EDAF4EE57",
"EFC2B3DB87FF4F00C79DFA8F732A23C0E18587A73A839B7710234583CDD03DB9",
"F1B6FE8FC2AB800E6D76EE975A002D3E67A60B51A62085A07289505B8D03F149",
"E827331B1FE7A2689FBC23D14CD21317C699596CBCA222182A489322ECE1FA74"),
6) shouldEqual
hashViaMerklePath(
hashViaMerklePath(
"dce52948923f07840400d52cc5deb037c8cef400a2e97699146a291112477ce0",
Seq("F1B6FE8FC2AB800E6D76EE975A002D3E67A60B51A62085A07289505B8D03F149"),
1),
Seq("E827331B1FE7A2689FBC23D14CD21317C699596CBCA222182A489322ECE1FA74"),
0)

verifySimplified(
"D97A21CF46FD5AFB0BF9EA4237BC4BF5C84E8B47D38D1EEE2BBEB5C0F8A1C625",
"79f56ece2f6f9082bf36c6f131bbe85ac4a3f0c5a07527e29f19e78c5bc281f8",
Seq(
"AE1E670BDBF8AB984F412E6102C369AECA2CED933A1DE74712CCDA5EDAF4EE57",
"EFC2B3DB87FF4F00C79DFA8F732A23C0E18587A73A839B7710234583CDD03DB9",
"F1B6FE8FC2AB800E6D76EE975A002D3E67A60B51A62085A07289505B8D03F149",
"E827331B1FE7A2689FBC23D14CD21317C699596CBCA222182A489322ECE1FA74"),
6) shouldEqual true

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,38 @@ package com.github.fluency03.blockchain.core

import org.scalatest.{FlatSpec, Matchers}

import scala.collection.mutable

class WalletTest extends FlatSpec with Matchers {

"Wallet" should "do something." in {
"RandomWallet" should "maintain a set of keys in random way." in {
val wallet = RandomWallet()
wallet shouldBe a[Wallet]
wallet.size() shouldEqual 1
val kc1 = wallet.keys.values.head

wallet.getKey(kc1.publicKeyHex) shouldEqual Some(kc1)
wallet.getKey("") shouldEqual None

val kc2 = wallet.newKey()
kc2 shouldBe a[KeyContainer]
wallet.size() shouldEqual 2
wallet.getKey(kc2.publicKeyHex) shouldEqual Some(kc2)

val uTxOs: mutable.Map[Outpoint, TxOut] = mutable.Map.empty[Outpoint, TxOut]
wallet.balance(uTxOs) shouldEqual 0

uTxOs += (Outpoint("def0", 0) -> TxOut(kc1.publicKeyHex, 40))
uTxOs += (Outpoint("def0", 1) -> TxOut("abc4", 40))
wallet.balance(uTxOs) shouldEqual 40
kc1.balance(uTxOs) shouldEqual 40
kc2.balance(uTxOs) shouldEqual 0

uTxOs += (Outpoint("def0", 2) -> TxOut(kc2.publicKeyHex, 40))
wallet.balance(uTxOs) shouldEqual 80

kc1.balance(uTxOs) shouldEqual 40
kc2.balance(uTxOs) shouldEqual 40
}

}

0 comments on commit f997aaa

Please sign in to comment.