Skip to content

Commit 00e44cf

Browse files
committedFeb 16, 2017
Day 5 数据序列化
1 parent 90c141e commit 00e44cf

20 files changed

+875
-143
lines changed
 

‎build.gradle

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
buildscript {
2-
ext.kotlin_version = '1.0.6'
2+
ext.kotlin_version = "1.0.6"
33

44
repositories {
55
mavenCentral()
@@ -14,9 +14,9 @@ buildscript {
1414
apply plugin: "kotlin"
1515
apply plugin: "application"
1616

17-
mainClassName = 'demo.HelloWorldKt'
17+
mainClassName = "demo.HelloWorldKt"
1818

19-
defaultTasks 'run'
19+
defaultTasks "run"
2020

2121
repositories {
2222
mavenCentral()
@@ -25,10 +25,12 @@ repositories {
2525
dependencies {
2626
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
2727
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
28-
compile 'joda-time:joda-time:2.9.7'
29-
compile 'com.madgag.spongycastle:core:1.54.0.0'
30-
compile 'com.madgag.spongycastle:prov:1.54.0.0'
31-
testCompile 'junit:junit:4.12'
28+
compile "joda-time:joda-time:2.9.7"
29+
compile "com.madgag.spongycastle:core:1.54.0.0"
30+
compile "com.madgag.spongycastle:prov:1.54.0.0"
31+
compile "com.typesafe:config:1.3.1"
32+
compile "org.iq80.leveldb:leveldb:0.9"
33+
testCompile "junit:junit:4.12"
3234
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
3335
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
3436
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package mbc.config
2+
3+
import com.typesafe.config.Config
4+
import com.typesafe.config.ConfigFactory
5+
import mbc.core.AccountState
6+
import mbc.core.Block
7+
import mbc.core.Transaction
8+
import mbc.serialization.AccountStateSerialize
9+
import mbc.serialization.BlockSerialize
10+
import mbc.serialization.TransactionSerialize
11+
import mbc.storage.DataSource
12+
import mbc.storage.LevelDbDataSource
13+
import mbc.storage.MemoryDataSource
14+
import mbc.storage.ObjectStore
15+
import org.spongycastle.util.encoders.Hex
16+
import java.io.File
17+
18+
class BlockChainConfig {
19+
20+
enum class DATABASE_TYPE {
21+
MEMORY, LEVELDB
22+
}
23+
24+
companion object {
25+
var minerCoinBase: ByteArray = ByteArray(0)
26+
27+
var defaultConfig: Config = ConfigFactory.empty()
28+
29+
fun getConfig(): Config {
30+
if (defaultConfig.isEmpty) {
31+
val resourceConfig = ConfigFactory.parseResources("application.conf")
32+
val fileConfig = ConfigFactory.parseFile(File("application.conf"))
33+
34+
defaultConfig = defaultConfig.withFallback(resourceConfig).withFallback(fileConfig)
35+
} else {
36+
return defaultConfig
37+
}
38+
return defaultConfig
39+
}
40+
41+
/**
42+
* Account State的存储类组装。
43+
*/
44+
fun getAccountStateStore(): ObjectStore<AccountState> {
45+
val dbName = "accounts"
46+
var ds: DataSource<ByteArray, ByteArray> = MemoryDataSource(dbName)
47+
if (getDatabaseType().equals(DATABASE_TYPE.LEVELDB.name, true)) {
48+
ds = LevelDbDataSource(dbName)
49+
}
50+
ds.init()
51+
return ObjectStore(ds, AccountStateSerialize())
52+
}
53+
54+
/**
55+
* Block的存储类组装。
56+
*/
57+
fun getBlockStore(): ObjectStore<Block> {
58+
val dbName = "blocks"
59+
var ds: DataSource<ByteArray, ByteArray> = MemoryDataSource(dbName)
60+
if (getDatabaseType().equals(DATABASE_TYPE.LEVELDB.name, true)) {
61+
ds = LevelDbDataSource(dbName)
62+
}
63+
ds.init()
64+
return ObjectStore(ds, BlockSerialize())
65+
}
66+
67+
/**
68+
* Transaction的存储类组装。
69+
*/
70+
fun getTransactionStore(): ObjectStore<Transaction> {
71+
val dbName = "transactions"
72+
var ds: DataSource<ByteArray, ByteArray> = MemoryDataSource(dbName)
73+
if (getDatabaseType().equals(DATABASE_TYPE.LEVELDB.name, true)) {
74+
ds = LevelDbDataSource(dbName)
75+
}
76+
ds.init()
77+
return ObjectStore(ds, TransactionSerialize())
78+
}
79+
80+
/**
81+
* 得到矿工的Coinbase地址。
82+
*/
83+
fun getMinerCoinbase(): ByteArray {
84+
if (this.minerCoinBase.size > 0) {
85+
return minerCoinBase
86+
} else {
87+
if (getConfig().hasPathOrNull("miner.minerCoinBase")) {
88+
minerCoinBase = Hex.decode(getConfig().getString("miner.minerCoinBase"))
89+
return minerCoinBase;
90+
} else {
91+
throw RuntimeException("Miner Coinbase is empty")
92+
}
93+
}
94+
}
95+
96+
/**
97+
* 设置矿工的Coinbase地址。
98+
*/
99+
fun setMinerCoinbase(newCoinbase: ByteArray) {
100+
this.minerCoinBase = newCoinbase
101+
}
102+
103+
/**
104+
* Key-Value存储的实现。
105+
*/
106+
fun getDatabaseType(): String {
107+
if (getConfig().hasPathOrNull("database.type")) {
108+
return getConfig().getString("database.type")
109+
} else {
110+
return DATABASE_TYPE.MEMORY.name
111+
}
112+
}
113+
114+
/**
115+
* 数据库的存储目录。
116+
*/
117+
fun getDatabaseDir(): String {
118+
if (getConfig().hasPathOrNull("database.dir")) {
119+
return getConfig().getString("database.dir")
120+
} else {
121+
return "database"
122+
}
123+
}
124+
125+
}
126+
127+
}

‎src/main/kotlin/mbc/core/Account.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package mbc.core
22

3+
import mbc.storage.Repository
34
import mbc.util.CryptoUtil
5+
import java.math.BigInteger
46
import java.security.PublicKey
57

68
/**
@@ -9,10 +11,10 @@ import java.security.PublicKey
911
* 账户地址(address)可以由公钥运算推导,比特币和以太坊均采用了类似机制。
1012
* 账户余额(balance)通常会保存在文件或数据库内。
1113
*/
12-
data class Account(val publicKey: PublicKey) {
13-
val address: String
14+
class Account(val publicKey: PublicKey) {
15+
val address: ByteArray
1416
get() = CryptoUtil.generateAddress(publicKey)
1517

16-
val balance: Long
17-
get() = AccountState.getAccountBalance(address)
18+
val balance: BigInteger
19+
get() = Repository.getBalance(address)
1820
}
Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,23 @@
11
package mbc.core
22

3-
import java.util.*
3+
import mbc.util.CodecUtil
4+
import java.math.BigInteger
45

56
/**
6-
* 账户状态管理
7+
* 账户状态
78
*/
8-
object AccountState {
9-
/**
10-
* 账户余额存储,为了演示方便使用HashMap。
11-
*/
12-
val accountBalances: HashMap<String, Long> = HashMap()
9+
class AccountState(val nonce: BigInteger, val balance: BigInteger) {
1310

14-
/**
15-
* 根据账户地址(address)读取账户余额(balance)。
16-
*/
17-
fun getAccountBalance(address: String): Long {
18-
return accountBalances.getOrDefault(address, 0)
11+
fun encode(): ByteArray {
12+
return CodecUtil.encodeAccountState(this)
1913
}
2014

21-
/**
22-
* 根据账户地址(address)更新账户余额(balance)。
23-
*/
24-
fun setAccountBalance(address: String, amount: Long): Long? {
25-
return accountBalances.put(address, amount)
15+
fun increaseNonce(): AccountState {
16+
return AccountState(nonce + BigInteger.ONE, balance)
2617
}
2718

19+
20+
fun increaseBalance(amount: BigInteger): AccountState {
21+
return AccountState(nonce, balance + amount)
22+
}
2823
}

‎src/main/kotlin/mbc/core/Block.kt

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
package mbc.core
22

3+
import mbc.util.CodecUtil
34
import mbc.util.CryptoUtil
45
import org.joda.time.DateTime
56

67
/**
7-
* 区块(Block)类,包含了区块高度(height),上一个区块哈希值(parentHash),旷工账户地址(minerAddress),交易列表(transactions)和时间戳(time)。
8+
* 区块(Block)类,包含了区块高度(height),上一个区块哈希值(parentHash),旷工账户地址(coinBase),交易列表(transactions)和时间戳(time)。
89
*/
9-
class Block(val height: Long, val parentHash: ByteArray, val minerAddress: String, val transactions: List<Transaction>,
10-
val time: DateTime) {
11-
12-
val version: Int = 1
13-
14-
val merkleRoot: ByteArray
15-
get() = CryptoUtil.merkleRoot(transactions)
16-
17-
var difficulty: Int = 0
18-
19-
var nonce: Int = 0
10+
class Block(val version: Int, val height: Long, val parentHash: ByteArray, val merkleRoot: ByteArray,
11+
val coinBase: ByteArray, val transactions: List<Transaction>, val time: DateTime, val difficulty: Int,
12+
val nonce: Int) {
2013

2114
/**
2215
* 区块(Block)的哈希值(KECCAK-256)
2316
*/
2417
val hash: ByteArray
2518
get() = CryptoUtil.hashBlock(this)
2619

20+
fun encode(): ByteArray {
21+
return CodecUtil.encodeBlock(this)
22+
}
23+
2724
}

‎src/main/kotlin/mbc/core/BlockChain.kt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
package mbc.core
22

3-
import mbc.core.TransactionExecutor.applyTrx
3+
import mbc.config.BlockChainConfig
4+
import mbc.core.TransactionExecutor.execute
5+
import mbc.util.CryptoUtil
46
import org.joda.time.DateTime
57

68
/**
79
* 区块链(BlockChain)管理类,负责区块的生成、发布、计算等功能。
810
*/
9-
class BlockChain(val minerAddress: String) {
11+
class BlockChain {
1012

1113
/**
1214
* 构造新的区块,要素信息为:区块高度(height),父区块的哈希值(parentHash), 交易记录(transactions),时间戳(time)
1315
*/
14-
fun createNewBlock(parent: Block, transactions: List<Transaction>): Block {
15-
val block = Block(parent.height + 1, parent.hash, minerAddress, transactions, DateTime())
16+
fun createNewBlock(version: Int, parent: Block, transactions: List<Transaction>): Block {
17+
val block = Block(version, parent.height + 1, parent.hash, CryptoUtil.merkleRoot(transactions),
18+
BlockChainConfig.getMinerCoinbase(),
19+
transactions, DateTime(), 0, 0)
1620

1721
for (trx in transactions) {
18-
applyTrx(trx)
22+
execute(trx)
1923
}
2024

2125
return block

‎src/main/kotlin/mbc/core/Transaction.kt

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package mbc.core
22

3+
import mbc.util.CodecUtil
34
import mbc.util.CryptoUtil
45
import org.joda.time.DateTime
6+
import java.math.BigInteger
57
import java.security.PrivateKey
68
import java.security.PublicKey
9+
import java.util.*
710

811
/**
912
* 交易记录类:记录了发送方(sender)向接受方(receiver)的转账记录,包括金额(amount)和时间戳(time)。
1013
* 为简化模型,没有加入费用(fee)。
1114
*/
12-
class Transaction(val senderAddress: String, val receiverAddress: String, val amount: Long,
13-
val time: DateTime, val publicKey: PublicKey) {
15+
class Transaction(val senderAddress: ByteArray, val receiverAddress: ByteArray, val amount: BigInteger,
16+
val time: DateTime, val publicKey: PublicKey) {
1417

1518
/**
1619
* 签名数据,初始值为byte[0]
@@ -31,4 +34,21 @@ class Transaction(val senderAddress: String, val receiverAddress: String, val am
3134
return signature
3235
}
3336

37+
fun encode(): ByteArray {
38+
return CodecUtil.encodeTransaction(this)
39+
}
40+
41+
override fun equals(o: Any?): Boolean {
42+
if (o is Transaction) {
43+
if (!Arrays.equals(this.senderAddress, o.senderAddress)) return false
44+
if (!Arrays.equals(this.receiverAddress, o.receiverAddress)) return false
45+
if (this.amount != o.amount) return false
46+
if (this.time != o.time) return false
47+
if (this.publicKey != o.publicKey) return false
48+
49+
return true
50+
} else {
51+
return false
52+
}
53+
}
3454
}

‎src/main/kotlin/mbc/core/TransactionExecutor.kt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package mbc.core
22

3+
import mbc.storage.Repository
4+
import java.math.BigInteger
5+
36
/**
47
* 交易处理并更新账户状态。
58
*/
@@ -8,19 +11,30 @@ object TransactionExecutor {
811
/**
912
* 增加账户余额(balance),如果amount为负数则余额减少。
1013
*/
11-
fun addAmount(address: String, amount: Long) {
12-
val newBalance = AccountState.getAccountBalance(address) + amount
13-
AccountState.setAccountBalance(address, newBalance)
14+
fun addBalance(address: ByteArray, amount: BigInteger) {
15+
Repository.addBalance(address, amount)
16+
}
17+
18+
/**
19+
* 转账功能,发送方减少金额,接收方增加金额。
20+
*/
21+
fun transfer(fromAddress: ByteArray, toAddress: ByteArray, amount: BigInteger) {
22+
addBalance(fromAddress, amount.negate())
23+
addBalance(toAddress, amount)
1424
}
1525

1626
/**
1727
* 根据交易记录更新区块链的状态(state),发送方的余额会减少,接收方的余额会增加。
1828
* 区块链的状态更新应该是原子操作,持久层是数据库可以使用事务功能。
1929
*/
20-
fun applyTrx(trx: Transaction) {
30+
fun execute(trx: Transaction) {
2131
if (trx.isValid) {
22-
addAmount(trx.senderAddress, -trx.amount)
23-
addAmount(trx.receiverAddress, +trx.amount)
32+
33+
// 发送方的Nonce+1
34+
Repository.increaseNonce(trx.senderAddress)
35+
36+
// 执行转账
37+
transfer(trx.senderAddress, trx.receiverAddress, trx.amount)
2438
} else {
2539
throw IllegalTransactionException()
2640
}

‎src/main/kotlin/mbc/miner/BlockMiner.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object BlockMiner {
2020
val ver = block.version
2121
val parentHash = block.parentHash
2222
val merkleRoot = block.merkleRoot
23-
val time = (block.time.millis/1000).toInt() // Current timestamp as seconds since 1970-01-01T00:00 UTC
23+
val time = (block.time.millis / 1000).toInt() // Current timestamp as seconds since 1970-01-01T00:00 UTC
2424
val difficulty = currentDifficulty // difficulty
2525

2626
// 挖矿难度的算法:https://en.bitcoin.it/wiki/Difficulty

‎src/main/kotlin/mbc/miner/MineResult.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ package mbc.miner
33
/**
44
* 挖矿的结果数据。https://en.bitcoin.it/wiki/Block_hashing_algorithm
55
*/
6-
data class MineResult(val target: Int, val nonce: Int)
6+
data class MineResult(val difficulty: Int, val nonce: Int)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package mbc.serialization
2+
3+
import mbc.core.AccountState
4+
import mbc.core.Block
5+
import mbc.core.Transaction
6+
import mbc.util.CodecUtil
7+
8+
/**
9+
* 序列化/反序列化接口。
10+
*/
11+
interface Serializer<T, S> {
12+
/**
13+
* Converts T ==> S
14+
* Should correctly handle null parameter
15+
*/
16+
fun serialize(obj: T): S
17+
18+
/**
19+
* Converts S ==> T
20+
* Should correctly handle null parameter
21+
*/
22+
fun deserialize(s: S): T?
23+
}
24+
25+
class AccountStateSerialize : Serializer<AccountState, ByteArray> {
26+
override fun deserialize(s: ByteArray): AccountState? {
27+
return CodecUtil.decodeAccountState(s)
28+
}
29+
30+
override fun serialize(obj: AccountState): ByteArray {
31+
return CodecUtil.encodeAccountState(obj)
32+
}
33+
34+
}
35+
36+
class BlockSerialize : Serializer<Block, ByteArray> {
37+
override fun deserialize(s: ByteArray): Block? {
38+
return CodecUtil.decodeBlock(s)
39+
}
40+
41+
override fun serialize(obj: Block): ByteArray {
42+
return CodecUtil.encodeBlock(obj)
43+
}
44+
45+
}
46+
47+
class TransactionSerialize : Serializer<Transaction, ByteArray> {
48+
override fun deserialize(s: ByteArray): Transaction? {
49+
return CodecUtil.decodeTransaction(s)
50+
}
51+
52+
override fun serialize(obj: Transaction): ByteArray {
53+
return CodecUtil.encodeTransaction(obj)
54+
}
55+
56+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package mbc.storage
2+
3+
/**
4+
* 数据源类,具体实现可以是Memory,LevelDb等。
5+
*/
6+
interface DataSource<K, V> {
7+
/**
8+
* 数据源的名字。
9+
*/
10+
val name: String
11+
12+
/**
13+
* 根据Key获得Value
14+
* @return 如果成功返回Value,如果失败返回Null。
15+
*/
16+
fun get(key: K): V?
17+
18+
/**
19+
* 写入Key-Value对。
20+
*/
21+
fun put(key: K, value: V)
22+
23+
/**
24+
* 删除Key-Value对。
25+
*/
26+
fun delete(key: K)
27+
28+
/**
29+
* 持久化数据。
30+
* @return 执行成功返回true,执行失败返回false
31+
*/
32+
fun flush(): Boolean
33+
34+
/**
35+
* 初始化数据库。
36+
*/
37+
fun init()
38+
39+
/**
40+
* @return 如果数据库可用则返回true
41+
*/
42+
fun isAlive(): Boolean
43+
44+
/**
45+
* 关闭数据库。
46+
*/
47+
fun close()
48+
49+
/**
50+
* 批量写入Key-Value。
51+
*/
52+
fun updateBatch(rows: Map<K, V>)
53+
54+
/**
55+
* @return 数据库的Keys,如果不支持该操作返回Null
56+
*/
57+
fun keys(): Set<K>
58+
59+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package mbc.storage
2+
3+
import mbc.config.BlockChainConfig
4+
import org.iq80.leveldb.CompressionType
5+
import org.iq80.leveldb.DB
6+
import org.iq80.leveldb.Options
7+
import org.iq80.leveldb.impl.Iq80DBFactory
8+
import java.io.File
9+
import java.util.*
10+
11+
/**
12+
* 内存数据源实现。
13+
*/
14+
class LevelDbDataSource(dbName: String) : DataSource<ByteArray, ByteArray> {
15+
16+
lateinit var db: DB
17+
var alive: Boolean = false
18+
19+
override val name = dbName
20+
21+
override fun put(key: ByteArray, value: ByteArray) {
22+
db.put(key, value)
23+
}
24+
25+
override fun get(key: ByteArray): ByteArray? {
26+
return db[key]
27+
}
28+
29+
override fun delete(key: ByteArray) {
30+
db.delete(key)
31+
}
32+
33+
override fun flush(): Boolean {
34+
return true
35+
}
36+
37+
override fun init() {
38+
if (isAlive()) return
39+
40+
val options = Options()
41+
options.createIfMissing(true)
42+
options.compressionType(CompressionType.NONE)
43+
options.blockSize(10 * 1024 * 1024)
44+
options.writeBufferSize(10 * 1024 * 1024)
45+
options.cacheSize(0)
46+
options.paranoidChecks(true)
47+
options.verifyChecksums(true)
48+
options.maxOpenFiles(32)
49+
50+
val databaseDir = File(BlockChainConfig.getDatabaseDir())
51+
if (!databaseDir.exists() || !databaseDir.isDirectory) {
52+
databaseDir.mkdirs()
53+
54+
val factory = Iq80DBFactory.factory
55+
db = factory.open(File(databaseDir.absolutePath + File.separator + name), options)
56+
57+
alive = true
58+
}
59+
}
60+
61+
override fun isAlive(): Boolean {
62+
return alive
63+
}
64+
65+
override fun close() {
66+
db.close()
67+
}
68+
69+
override fun updateBatch(rows: Map<ByteArray, ByteArray>) {
70+
for ((key, value) in rows) {
71+
put(key, value)
72+
}
73+
}
74+
75+
override fun keys(): Set<ByteArray> {
76+
db.iterator().use { iterator ->
77+
val result = HashSet<ByteArray>()
78+
iterator.seekToFirst()
79+
while (iterator.hasNext()) {
80+
result.add(iterator.peekNext().key)
81+
iterator.next()
82+
}
83+
return result
84+
}
85+
}
86+
87+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package mbc.storage
2+
3+
import org.spongycastle.util.encoders.Hex
4+
5+
/**
6+
* 内存数据源实现。
7+
*/
8+
class MemoryDataSource(dbName: String) : DataSource<ByteArray, ByteArray> {
9+
10+
override val name = dbName
11+
12+
val db = mutableMapOf<String, ByteArray>()
13+
14+
override fun put(key: ByteArray, value: ByteArray) {
15+
db.put(Hex.toHexString(key), value)
16+
}
17+
18+
override fun get(key: ByteArray): ByteArray? {
19+
return db[Hex.toHexString(key)]
20+
}
21+
22+
override fun delete(key: ByteArray) {
23+
db.remove(Hex.toHexString(key))
24+
}
25+
26+
override fun flush(): Boolean {
27+
return true
28+
}
29+
30+
override fun init() {}
31+
32+
override fun isAlive(): Boolean {
33+
return true
34+
}
35+
36+
override fun close() {}
37+
38+
override fun updateBatch(rows: Map<ByteArray, ByteArray>) {
39+
for ((key, value) in rows) {
40+
put(key, value)
41+
}
42+
}
43+
44+
override fun keys(): Set<ByteArray> {
45+
val stringKeys = db.keys
46+
val result = mutableSetOf<ByteArray>()
47+
stringKeys.map { result.add(Hex.decode(it)) }
48+
return result
49+
}
50+
51+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package mbc.storage
2+
3+
import mbc.serialization.Serializer
4+
5+
/**
6+
* 对象存储类,可以接入不同的DbSource(Memory, LevelDb)和Serializer(AccountState, Transaction, Block)实现。
7+
*/
8+
class ObjectStore<V>(val db: DataSource<ByteArray, ByteArray>, val serializer: Serializer<V, ByteArray>) {
9+
fun put(k: ByteArray, v: V) {
10+
db.put(k, serializer.serialize(v))
11+
}
12+
13+
14+
fun get(k: ByteArray): V? {
15+
val bytes = db.get(k)
16+
if (bytes == null) {
17+
return null
18+
} else {
19+
return serializer.deserialize(bytes)
20+
}
21+
}
22+
23+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package mbc.storage
2+
3+
import mbc.config.BlockChainConfig
4+
import mbc.core.AccountState
5+
import java.math.BigInteger
6+
7+
/**
8+
* 管理区块链状态的管理类(Account State, Blocks和Transactions)
9+
*/
10+
object Repository {
11+
12+
/**
13+
* Account State Db.
14+
*/
15+
val accountStateStore = BlockChainConfig.getAccountStateStore()
16+
17+
/**
18+
* Blocks Db.
19+
*/
20+
val blockStore = BlockChainConfig.getBlockStore()
21+
22+
/**
23+
* Transactions Db.
24+
*/
25+
val transactionStore = BlockChainConfig.getTransactionStore()
26+
27+
/**
28+
* 读取账户余额。
29+
*/
30+
fun getBalance(address: ByteArray): BigInteger {
31+
return accountStateStore.get(address)?.balance ?: BigInteger.ZERO
32+
}
33+
34+
/**
35+
* 账户的Nonce+1
36+
*/
37+
fun increaseNonce(address: ByteArray) {
38+
val accountState = getOrCreateAccountState(address)
39+
return accountStateStore.put(address, accountState.increaseNonce())
40+
}
41+
42+
/**
43+
* 增加账户余额。
44+
*/
45+
fun addBalance(address: ByteArray, amount: BigInteger) {
46+
val accountState = getOrCreateAccountState(address)
47+
return accountStateStore.put(address, accountState.increaseBalance(amount))
48+
}
49+
50+
/**
51+
* 新建账户。
52+
*/
53+
private fun createAccountState(address: ByteArray): AccountState {
54+
val state = AccountState(BigInteger.ZERO, BigInteger.ZERO)
55+
accountStateStore.put(address, state)
56+
return state
57+
}
58+
59+
/**
60+
* 判断账户状态Account State是不是存在,如果不存在就新建账户。
61+
*/
62+
private fun getOrCreateAccountState(address: ByteArray): AccountState {
63+
var ret = accountStateStore.get(address)
64+
if (ret == null) {
65+
ret = createAccountState(address)
66+
}
67+
return ret
68+
}
69+
70+
}

‎src/main/kotlin/mbc/util/CodecUtil.kt

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package mbc.util
2+
3+
import mbc.core.AccountState
4+
import mbc.core.Block
5+
import mbc.core.Transaction
6+
import org.joda.time.DateTime
7+
import org.spongycastle.asn1.*
8+
import java.security.KeyFactory
9+
import java.security.spec.X509EncodedKeySpec
10+
11+
12+
object CodecUtil {
13+
/**
14+
* 序列化账户状态(Account State)。(使用ASN.1规范)
15+
*/
16+
fun encodeAccountState(accountState: AccountState): ByteArray {
17+
val v = ASN1EncodableVector()
18+
19+
v.add(ASN1Integer(accountState.nonce))
20+
v.add(ASN1Integer(accountState.balance))
21+
22+
return DERSequence(v).encoded
23+
}
24+
25+
/**
26+
* 反序列化账户状态(Account State)。(使用ASN.1规范)
27+
*/
28+
fun decodeAccountState(bytes: ByteArray): AccountState? {
29+
val v = ASN1InputStream(bytes)?.readObject()
30+
31+
if (v != null) {
32+
val seq = ASN1Sequence.getInstance(v)
33+
val nonce = ASN1Integer.getInstance(seq.getObjectAt(0))?.value
34+
val balance = ASN1Integer.getInstance(seq.getObjectAt(1))?.value
35+
36+
if (nonce != null && balance != null) {
37+
return AccountState(nonce, balance)
38+
}
39+
}
40+
41+
return null
42+
}
43+
44+
/**
45+
* 序列化交易(Transaction)。(使用ASN.1规范)
46+
*/
47+
fun encodeTransaction(trx: Transaction): ByteArray {
48+
val v = ASN1EncodableVector()
49+
50+
v.add(DERBitString(trx.senderAddress))
51+
v.add(DERBitString(trx.receiverAddress))
52+
v.add(ASN1Integer(trx.amount))
53+
v.add(ASN1Integer(trx.time.millis))
54+
v.add(DERBitString(trx.publicKey.encoded))
55+
56+
return DERSequence(v).encoded
57+
}
58+
59+
/**
60+
* 反序列化交易(Transaction)。(使用ASN.1规范)
61+
*/
62+
fun decodeTransaction(bytes: ByteArray): Transaction? {
63+
val v = ASN1InputStream(bytes)?.readObject()
64+
65+
if (v != null) {
66+
val seq = ASN1Sequence.getInstance(v)
67+
val senderAddress = DERBitString.getInstance(seq.getObjectAt(0))?.bytes
68+
val receiverAddress = DERBitString.getInstance(seq.getObjectAt(1))?.bytes
69+
val amount = ASN1Integer.getInstance(seq.getObjectAt(2))?.value
70+
val millis = ASN1Integer.getInstance(seq.getObjectAt(3))?.value
71+
val publicKeyBytes = DERBitString.getInstance(seq.getObjectAt(4))?.bytes
72+
73+
val kf = KeyFactory.getInstance("EC", "SC")
74+
val publicKey = kf.generatePublic(X509EncodedKeySpec(publicKeyBytes))
75+
76+
if (senderAddress != null && receiverAddress != null && amount != null && millis != null) {
77+
return Transaction(senderAddress, receiverAddress, amount, DateTime(millis.toLong()), publicKey)
78+
}
79+
}
80+
81+
return null
82+
}
83+
84+
/**
85+
* 序列化区块(Block)。(使用ASN.1规范)
86+
*/
87+
fun encodeBlock(block: Block): ByteArray {
88+
89+
val v = ASN1EncodableVector()
90+
91+
v.add(ASN1Integer(block.version.toLong()))
92+
v.add(ASN1Integer(block.height))
93+
v.add(DERBitString(block.parentHash))
94+
v.add(DERBitString(block.coinBase))
95+
v.add(DERBitString(block.merkleRoot))
96+
v.add(ASN1Integer(block.difficulty.toLong()))
97+
v.add(ASN1Integer(block.nonce.toLong()))
98+
v.add(ASN1Integer(block.time.millis))
99+
100+
v.add(ASN1Integer(block.transactions.size.toLong()))
101+
102+
val t = ASN1EncodableVector()
103+
block.transactions.map { t.add(ASN1InputStream(encodeTransaction(it)).readObject()) } // transactions
104+
v.add(DERSequence(t))
105+
106+
return DERSequence(v).encoded
107+
}
108+
109+
/**
110+
* 反序列化区块(Block)。(使用ASN.1规范)
111+
*/
112+
fun decodeBlock(bytes: ByteArray): Block? {
113+
val v = ASN1InputStream(bytes)?.readObject()
114+
115+
if (v != null) {
116+
val seq = ASN1Sequence.getInstance(v)
117+
val version = ASN1Integer.getInstance(seq.getObjectAt(0)).value
118+
val height = ASN1Integer.getInstance(seq.getObjectAt(1)).value
119+
val parentHash = DERBitString.getInstance(seq.getObjectAt(2))?.bytes
120+
val minerAddress = DERBitString.getInstance(seq.getObjectAt(3))?.bytes
121+
val merkleRoot = DERBitString.getInstance(seq.getObjectAt(4))?.bytes
122+
val difficulty = ASN1Integer.getInstance(seq.getObjectAt(5))?.value
123+
val nonce = ASN1Integer.getInstance(seq.getObjectAt(6))?.value
124+
val millis = ASN1Integer.getInstance(seq.getObjectAt(7))?.value
125+
126+
val trxSize = ASN1Integer.getInstance(seq.getObjectAt(8))?.value
127+
128+
val trxValues = ASN1Sequence.getInstance(seq.getObjectAt(9))
129+
130+
val trxList = mutableListOf<Transaction>()
131+
132+
for (trxValue in trxValues.objects) {
133+
val trxObj = DERSequence.getInstance(trxValue) ?: return null
134+
val trx = decodeTransaction(trxObj.encoded) ?: return null
135+
trxList.add(trx)
136+
}
137+
138+
if (version == null || height == null || parentHash == null || minerAddress == null ||
139+
merkleRoot == null || difficulty == null || nonce == null || millis == null) {
140+
return null
141+
}
142+
143+
return Block(version.toInt(), height.toLong(), parentHash, CryptoUtil.merkleRoot(trxList), minerAddress, trxList,
144+
DateTime(millis.toLong()), difficulty.toInt(), nonce.toInt())
145+
}
146+
147+
return null
148+
}
149+
150+
}

‎src/main/kotlin/mbc/util/CryptoUtil.kt

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@ package mbc.util
33
import mbc.core.Block
44
import mbc.core.Transaction
55
import org.spongycastle.jce.provider.BouncyCastleProvider
6-
import org.spongycastle.util.encoders.Hex
7-
import java.nio.ByteBuffer
86
import java.security.*
97
import java.security.Security.insertProviderAt
108
import java.security.spec.ECGenParameterSpec
11-
import java.util.*
129

1310
/**
1411
* 密码学工具类。
@@ -25,12 +22,12 @@ class CryptoUtil {
2522
* 比特币地址算法:http://www.infoq.com/cn/articles/bitcoin-and-block-chain-part03
2623
* 以太坊地址算法:http://ethereum.stackexchange.com/questions/3542/how-are-ethereum-addresses-generated
2724
*/
28-
fun generateAddress(publicKey: PublicKey): String {
25+
fun generateAddress(publicKey: PublicKey): ByteArray {
2926
val digest = MessageDigest.getInstance("KECCAK-256", "SC")
3027
digest.update(publicKey.encoded)
3128
val hash = digest.digest()
3229

33-
return Hex.toHexString(hash.drop(12).toByteArray())
30+
return hash.drop(12).toByteArray()
3431
}
3532

3633
/**
@@ -49,7 +46,7 @@ class CryptoUtil {
4946
fun signTransaction(trx: Transaction, privateKey: PrivateKey): ByteArray {
5047
val signer = Signature.getInstance("SHA256withECDSA")
5148
signer.initSign(privateKey)
52-
val msgToSign = encodeTransaction(trx)
49+
val msgToSign = trx.encode()
5350
signer.update(msgToSign)
5451
return signer.sign()
5552
}
@@ -61,7 +58,7 @@ class CryptoUtil {
6158
val signer = Signature.getInstance("SHA256withECDSA")
6259
signer.initVerify(trx.publicKey)
6360

64-
signer.update(encodeTransaction(trx))
61+
signer.update(trx.encode())
6562
return signer.verify(signature)
6663
}
6764

@@ -70,45 +67,10 @@ class CryptoUtil {
7067
*/
7168
fun hashBlock(block: Block): ByteArray {
7269
val digest = MessageDigest.getInstance("KECCAK-256", "SC")
73-
digest.update(encodeBlock(block))
70+
digest.update(block.encode())
7471
return digest.digest()
7572
}
7673

77-
/**
78-
* 序列化交易(Transaction)。当前实现非常简单,后期会改成以太坊的RLP协议。
79-
*/
80-
private fun encodeTransaction(
81-
trx: Transaction): ByteArray {
82-
val byteBuffer = ByteBuffer.allocate(1024)
83-
byteBuffer.put(Hex.decode(trx.senderAddress))
84-
byteBuffer.put(Hex.decode(trx.receiverAddress))
85-
byteBuffer.put(ByteBuffer.allocate(8).putLong(trx.amount).array())
86-
byteBuffer.put(ByteBuffer.allocate(4).putInt((trx.time.millis / 1000).toInt()).array())
87-
88-
val result = ByteArray(byteBuffer.remaining())
89-
byteBuffer.get(result, 0, result.size)
90-
return result
91-
}
92-
93-
/**
94-
* 序列化区块(Block)。当前实现非常简单,后期会改成以太坊的RLP协议。
95-
*/
96-
private fun encodeBlock(block: Block): ByteArray {
97-
val byteBuffer = ByteBuffer.allocate(1024)
98-
byteBuffer.put(ByteBuffer.allocate(4).putInt(block.version).array()) // version
99-
byteBuffer.put(Hex.decode(block.parentHash)) // parentHash
100-
byteBuffer.put(Hex.decode(block.merkleRoot)) // merkleRoot
101-
byteBuffer.put(ByteBuffer.allocate(4).putInt((block.time.millis / 1000).toInt()).array()) // time
102-
byteBuffer.put(ByteBuffer.allocate(4).putInt(block.difficulty).array()) // bits(current difficulty)
103-
byteBuffer.put(ByteBuffer.allocate(4).putInt(block.nonce).array()) // nonce
104-
105-
byteBuffer.put(ByteBuffer.allocate(4).putInt(block.transactions.size).array()) // transaction count
106-
107-
block.transactions.map { byteBuffer.put(encodeTransaction(it)) } // transactions
108-
109-
byteBuffer.flip()
110-
return byteBuffer.array()
111-
}
11274

11375
/**
11476
* 计算Merkle Root Hash
@@ -118,11 +80,9 @@ class CryptoUtil {
11880
if (count == 0) {
11981
return ByteArray(0)
12082
} else if (count == 1) {
121-
return sha256(sha256(
122-
encodeTransaction(transactions[0]) + encodeTransaction(transactions[0]))) // Double hash if we are leaf.
83+
return sha256(sha256(transactions[0].encode() + transactions[0].encode())) // Double hash if we are leaf.
12384
} else if (count == 2) {
124-
return sha256(sha256(
125-
encodeTransaction(transactions[0]) + encodeTransaction(transactions[1]))) // Double hash if we are leaf.
85+
return sha256(sha256(transactions[0].encode() + transactions[1].encode())) // Double hash if we are leaf.
12686
} else {
12787
if (count.mod(2) == 1) {
12888
val newList = transactions.toMutableList();

‎src/main/resources/application.conf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
database {
2+
type = "leveldb"
3+
dir = "database"
4+
}
5+
miner {
6+
coinbase = "0000000000000000000000000000000000000000"
7+
}

‎src/test/kotlin/mbc/core/BlockChainTest.kt

Lines changed: 149 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package mbc.core
22

3-
import mbc.core.TransactionExecutor.addAmount
4-
import mbc.core.TransactionExecutor.applyTrx
3+
import junit.framework.Assert.assertTrue
4+
import mbc.config.BlockChainConfig
5+
import mbc.core.TransactionExecutor.addBalance
6+
import mbc.core.TransactionExecutor.execute
57
import mbc.miner.BlockMiner
8+
import mbc.util.CodecUtil
69
import mbc.util.CryptoUtil
710
import mbc.util.CryptoUtil.Companion.generateKeyPair
811
import mbc.util.CryptoUtil.Companion.sha256
912
import mbc.util.CryptoUtil.Companion.verifyTransactionSignature
1013
import org.joda.time.DateTime
14+
import org.junit.Assert.assertArrayEquals
1115
import org.junit.Before
1216
import org.junit.Test
17+
import org.spongycastle.asn1.ASN1InputStream
18+
import org.spongycastle.asn1.util.ASN1Dump
1319
import org.spongycastle.jce.provider.BouncyCastleProvider
1420
import org.spongycastle.util.encoders.Hex
1521
import java.math.BigInteger
@@ -23,6 +29,13 @@ import kotlin.test.assertNotEquals
2329

2430

2531
class BlockChainTest {
32+
33+
val version = 1
34+
35+
val genesisBlock = Block(1, 0, ByteArray(0), CryptoUtil.merkleRoot(emptyList()),
36+
Hex.decode("1234567890123456789012345678901234567890"), emptyList(),
37+
DateTime(2017, 2, 1, 0, 0), 0, 0)
38+
2639
@Before fun setup() {
2740
Security.insertProviderAt(BouncyCastleProvider(), 1)
2841
}
@@ -34,7 +47,7 @@ class BlockChainTest {
3447
val keyPair = generateKeyPair() ?: return
3548

3649
val account = Account(keyPair.public)
37-
assert(account.address.length == 40)
50+
assert(account.address.size == 20)
3851
}
3952

4053
/**
@@ -50,20 +63,20 @@ class BlockChainTest {
5063
val bob = Account(kp2.public)
5164

5265
// 初始金额为200
53-
addAmount(alice.address, 200)
54-
addAmount(bob.address, 200)
66+
addBalance(alice.address, BigInteger.valueOf(200))
67+
addBalance(bob.address, BigInteger.valueOf(200))
5568

5669
// Alice向Bob转账100
57-
val trx = Transaction(alice.address, bob.address, 100, DateTime(), kp1.public)
70+
val trx = Transaction(alice.address, bob.address, BigInteger.valueOf(100), DateTime(), kp1.public)
5871
// Alice用私钥签名
5972
trx.sign(kp1.private)
6073

6174
// 根据交易记录更新区块链状态
62-
applyTrx(trx)
75+
execute(trx)
6376

6477
// 查询余额是否正确
65-
assert(alice.balance == 100L)
66-
assert(bob.balance == 300L)
78+
assert(alice.balance == BigInteger.valueOf(100))
79+
assert(bob.balance == BigInteger.valueOf(300))
6780
}
6881

6982
/**
@@ -109,7 +122,7 @@ class BlockChainTest {
109122
val bob = Account(kp2.public)
110123

111124
// Alice向Bob转账100
112-
val trx = Transaction(alice.address, bob.address, 100, DateTime(), kp1.public)
125+
val trx = Transaction(alice.address, bob.address, BigInteger.valueOf(100), DateTime(), kp1.public)
113126

114127
// Alice用私钥签名
115128
val signature = trx.sign(kp1.private)
@@ -134,29 +147,26 @@ class BlockChainTest {
134147
val bob = Account(kp2.public)
135148

136149
// 初始金额为200
137-
addAmount(alice.address, 200)
138-
addAmount(bob.address, 200)
150+
addBalance(alice.address, BigInteger.valueOf(200))
151+
addBalance(bob.address, BigInteger.valueOf(200))
139152

140153
// Alice向Bob转账100
141-
val trx = Transaction(alice.address, bob.address, 100, DateTime(), kp1.public)
154+
val trx = Transaction(alice.address, bob.address, BigInteger.valueOf(100), DateTime(), kp1.public)
142155
// Alice用私钥签名
143156
trx.sign(kp1.private)
144157

145158
// 初始化矿工Charlie账户
146159
val kp3 = generateKeyPair() ?: return
147160
val charlie = Account(kp3.public)
148161

149-
// 构造原始区块(高度为0)
150-
val genesisBlock = Block(0, ByteArray(0), "1234567890123456789012345678901234567890", emptyList(),
151-
DateTime(2017, 2, 1, 0, 0))
152-
153162
// 构造新的区块
154-
val blockChain = BlockChain(charlie.address)
155-
blockChain.createNewBlock(genesisBlock, listOf(trx))
163+
BlockChainConfig.setMinerCoinbase(charlie.address)
164+
val blockChain = BlockChain()
165+
blockChain.createNewBlock(version, genesisBlock, listOf(trx))
156166

157167
// 查询余额是否正确
158-
assert(alice.balance == 100L)
159-
assert(bob.balance == 300L)
168+
assert(alice.balance == BigInteger.valueOf(100))
169+
assert(bob.balance == BigInteger.valueOf(300))
160170
}
161171

162172
/**
@@ -179,7 +189,7 @@ class BlockChainTest {
179189
var nonce = 0
180190
while (nonce < 0x100000000) {
181191

182-
val headerBuffer = ByteBuffer.allocate(4+32+32+4+4+4)
192+
val headerBuffer = ByteBuffer.allocate(4 + 32 + 32 + 4 + 4 + 4)
183193
headerBuffer.put(ByteBuffer.allocate(4).putInt(ver).array()) // version
184194
headerBuffer.put(Hex.decode(parentHash)) // parentHash
185195
headerBuffer.put(Hex.decode(merkleRoot)) // merkleRoot
@@ -221,13 +231,13 @@ class BlockChainTest {
221231
val bob = Account(kp2.public)
222232

223233
// Alice向Bob转账100
224-
val trx1 = Transaction(alice.address, bob.address, 100, DateTime(), kp1.public)
234+
val trx1 = Transaction(alice.address, bob.address, BigInteger.valueOf(100), DateTime(), kp1.public)
225235

226236
// Alice用私钥签名
227237
val signature = trx1.sign(kp1.private)
228238

229239
// Alice向Bob转账50
230-
val trx2 = Transaction(alice.address, bob.address, 50, DateTime(), kp1.public)
240+
val trx2 = Transaction(alice.address, bob.address, BigInteger.valueOf(50), DateTime(), kp1.public)
231241

232242
// Alice用私钥签名
233243
val signature2 = trx2.sign(kp1.private)
@@ -237,7 +247,7 @@ class BlockChainTest {
237247
}
238248

239249
/**
240-
* 挖矿
250+
* 挖矿测试
241251
*/
242252
@Test fun mineBlockTest() {
243253
// 初始化Alice账户
@@ -249,37 +259,135 @@ class BlockChainTest {
249259
val bob = Account(kp2.public)
250260

251261
// 初始金额为200
252-
addAmount(alice.address, 200)
253-
addAmount(bob.address, 200)
262+
addBalance(alice.address, BigInteger.valueOf(200))
263+
addBalance(bob.address, BigInteger.valueOf(200))
254264

255265
// Alice向Bob转账100
256-
val trx = Transaction(alice.address, bob.address, 100, DateTime(), kp1.public)
266+
val trx = Transaction(alice.address, bob.address, BigInteger.valueOf(100), DateTime(), kp1.public)
257267
// Alice用私钥签名
258268
trx.sign(kp1.private)
259269

260270
// 初始化矿工Charlie账户
261271
val kp3 = generateKeyPair() ?: return
262272
val charlie = Account(kp3.public)
263273

264-
// 构造原始区块(高度为0)
265-
val genesisBlock = Block(0, ByteArray(0), "1234567890123456789012345678901234567890", emptyList(),
266-
DateTime(2017, 2, 1, 0, 0))
267-
268274
// 构造新的区块
269-
val blockChain = BlockChain(charlie.address)
270-
val block = blockChain.createNewBlock(genesisBlock, listOf(trx))
275+
BlockChainConfig.setMinerCoinbase(charlie.address)
276+
val blockChain = BlockChain()
277+
val block = blockChain.createNewBlock(version, genesisBlock, listOf(trx))
271278

272279
// 查询余额是否正确
273-
assert(alice.balance == 100L)
274-
assert(bob.balance == 300L)
280+
assert(alice.balance == BigInteger.valueOf(100))
281+
assert(bob.balance == BigInteger.valueOf(300))
275282

276283
val mineResult = BlockMiner.mine(block)
277-
block.difficulty = mineResult.target
278-
block.nonce = mineResult.nonce
284+
val minedBlock = Block(block.version, block.height, block.parentHash, block.merkleRoot, block.coinBase,
285+
block.transactions, block.time, mineResult.difficulty, mineResult.nonce)
286+
287+
println("Block nonce: ${minedBlock.nonce}")
288+
assertNotEquals(minedBlock.difficulty, 0)
289+
assertNotEquals(minedBlock.nonce, 0)
290+
}
291+
292+
/**
293+
* 账户状态序列化/反序列化测试。
294+
*/
295+
@Test fun accountStateEncodeTest() {
296+
val accountState = AccountState(BigInteger.TEN, BigInteger.TEN)
297+
298+
println(ASN1Dump.dumpAsString(ASN1InputStream(accountState.encode()).readObject()))
299+
300+
val decoded = CodecUtil.decodeAccountState(accountState.encode())
301+
302+
assertEquals(accountState.nonce, decoded?.nonce)
303+
assertEquals(accountState.balance, decoded?.balance)
304+
}
305+
306+
/**
307+
* 交易Transaction序列化/反序列化测试。
308+
*/
309+
@Test fun transactionEncodeTest() {
310+
// 初始化Alice账户
311+
val kp1 = generateKeyPair() ?: return
312+
val alice = Account(kp1.public)
313+
314+
// 初始化Bob账户
315+
val kp2 = generateKeyPair() ?: return
316+
val bob = Account(kp2.public)
317+
318+
// 初始金额为200
319+
addBalance(alice.address, BigInteger.valueOf(200))
320+
addBalance(bob.address, BigInteger.valueOf(200))
321+
322+
// Alice向Bob转账100
323+
val trx = Transaction(alice.address, bob.address, BigInteger.valueOf(100), DateTime(), kp1.public)
324+
// Alice用私钥签名
325+
trx.sign(kp1.private)
326+
327+
println(ASN1Dump.dumpAsString(ASN1InputStream(trx.encode()).readObject()))
279328

280-
println("Block nonce: ${block.nonce}")
281-
assertNotEquals(block.difficulty, 0)
282-
assertNotEquals(block.nonce, 0)
329+
val decoded = CodecUtil.decodeTransaction(trx.encode())
330+
331+
if (decoded == null) {
332+
return
333+
}
334+
335+
assertArrayEquals(trx.senderAddress, decoded.senderAddress)
336+
assertArrayEquals(trx.receiverAddress, decoded.receiverAddress)
337+
assertEquals(trx.amount, decoded.amount)
338+
assertEquals(trx.time.millis, decoded.time.millis)
339+
assertEquals(trx.publicKey, decoded.publicKey)
283340
}
284341

342+
/**
343+
* 区块Block序列化/反序列化测试。
344+
*/
345+
@Test fun blockEncodeTest() {
346+
// 初始化Alice账户
347+
val kp1 = generateKeyPair() ?: return
348+
val alice = Account(kp1.public)
349+
350+
// 初始化Bob账户
351+
val kp2 = generateKeyPair() ?: return
352+
val bob = Account(kp2.public)
353+
354+
// 初始金额为200
355+
addBalance(alice.address, BigInteger.valueOf(200))
356+
addBalance(bob.address, BigInteger.valueOf(200))
357+
358+
// Alice向Bob转账100
359+
val trx1 = Transaction(alice.address, bob.address, BigInteger.valueOf(100), DateTime(), kp1.public)
360+
361+
// Alice用私钥签名
362+
val signature = trx1.sign(kp1.private)
363+
364+
// Alice向Bob转账50
365+
val trx2 = Transaction(alice.address, bob.address, BigInteger.valueOf(50), DateTime(), kp1.public)
366+
367+
// Alice用私钥签名
368+
val signature2 = trx2.sign(kp1.private)
369+
370+
// 初始化矿工Charlie账户
371+
val kp3 = generateKeyPair() ?: return
372+
val charlie = Account(kp3.public)
373+
374+
// 构造新的区块
375+
BlockChainConfig.setMinerCoinbase(charlie.address)
376+
val blockChain = BlockChain()
377+
val block = blockChain.createNewBlock(version, genesisBlock, listOf(trx1, trx2))
378+
379+
println(ASN1Dump.dumpAsString(ASN1InputStream(block.encode()).readObject()))
380+
381+
val decoded = CodecUtil.decodeBlock(block.encode()) ?: return
382+
383+
assertEquals(block.version, decoded.version)
384+
assertEquals(block.height, decoded.height)
385+
assertArrayEquals(block.parentHash, decoded.parentHash)
386+
assertArrayEquals(block.merkleRoot, decoded.merkleRoot)
387+
assertArrayEquals(block.coinBase, decoded.coinBase)
388+
assertArrayEquals(block.transactions.toTypedArray(), decoded.transactions.toTypedArray())
389+
assertEquals(block.time, decoded.time)
390+
assertEquals(block.difficulty, decoded.difficulty)
391+
assertEquals(block.nonce, decoded.nonce)
392+
}
285393
}

0 commit comments

Comments
 (0)
Please sign in to comment.