Skip to content

Commit

Permalink
Day 5 数据序列化
Browse files Browse the repository at this point in the history
  • Loading branch information
qikh committed Feb 16, 2017
1 parent 90c141e commit 00e44cf
Show file tree
Hide file tree
Showing 20 changed files with 875 additions and 143 deletions.
16 changes: 9 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.0.6'
ext.kotlin_version = "1.0.6"

repositories {
mavenCentral()
Expand All @@ -14,9 +14,9 @@ buildscript {
apply plugin: "kotlin"
apply plugin: "application"

mainClassName = 'demo.HelloWorldKt'
mainClassName = "demo.HelloWorldKt"

defaultTasks 'run'
defaultTasks "run"

repositories {
mavenCentral()
Expand All @@ -25,10 +25,12 @@ repositories {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile 'joda-time:joda-time:2.9.7'
compile 'com.madgag.spongycastle:core:1.54.0.0'
compile 'com.madgag.spongycastle:prov:1.54.0.0'
testCompile 'junit:junit:4.12'
compile "joda-time:joda-time:2.9.7"
compile "com.madgag.spongycastle:core:1.54.0.0"
compile "com.madgag.spongycastle:prov:1.54.0.0"
compile "com.typesafe:config:1.3.1"
compile "org.iq80.leveldb:leveldb:0.9"
testCompile "junit:junit:4.12"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
}
Expand Down
127 changes: 127 additions & 0 deletions src/main/kotlin/mbc/config/BlockChainConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package mbc.config

import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import mbc.core.AccountState
import mbc.core.Block
import mbc.core.Transaction
import mbc.serialization.AccountStateSerialize
import mbc.serialization.BlockSerialize
import mbc.serialization.TransactionSerialize
import mbc.storage.DataSource
import mbc.storage.LevelDbDataSource
import mbc.storage.MemoryDataSource
import mbc.storage.ObjectStore
import org.spongycastle.util.encoders.Hex
import java.io.File

class BlockChainConfig {

enum class DATABASE_TYPE {
MEMORY, LEVELDB
}

companion object {
var minerCoinBase: ByteArray = ByteArray(0)

var defaultConfig: Config = ConfigFactory.empty()

fun getConfig(): Config {
if (defaultConfig.isEmpty) {
val resourceConfig = ConfigFactory.parseResources("application.conf")
val fileConfig = ConfigFactory.parseFile(File("application.conf"))

defaultConfig = defaultConfig.withFallback(resourceConfig).withFallback(fileConfig)
} else {
return defaultConfig
}
return defaultConfig
}

/**
* Account State的存储类组装。
*/
fun getAccountStateStore(): ObjectStore<AccountState> {
val dbName = "accounts"
var ds: DataSource<ByteArray, ByteArray> = MemoryDataSource(dbName)
if (getDatabaseType().equals(DATABASE_TYPE.LEVELDB.name, true)) {
ds = LevelDbDataSource(dbName)
}
ds.init()
return ObjectStore(ds, AccountStateSerialize())
}

/**
* Block的存储类组装。
*/
fun getBlockStore(): ObjectStore<Block> {
val dbName = "blocks"
var ds: DataSource<ByteArray, ByteArray> = MemoryDataSource(dbName)
if (getDatabaseType().equals(DATABASE_TYPE.LEVELDB.name, true)) {
ds = LevelDbDataSource(dbName)
}
ds.init()
return ObjectStore(ds, BlockSerialize())
}

/**
* Transaction的存储类组装。
*/
fun getTransactionStore(): ObjectStore<Transaction> {
val dbName = "transactions"
var ds: DataSource<ByteArray, ByteArray> = MemoryDataSource(dbName)
if (getDatabaseType().equals(DATABASE_TYPE.LEVELDB.name, true)) {
ds = LevelDbDataSource(dbName)
}
ds.init()
return ObjectStore(ds, TransactionSerialize())
}

/**
* 得到矿工的Coinbase地址。
*/
fun getMinerCoinbase(): ByteArray {
if (this.minerCoinBase.size > 0) {
return minerCoinBase
} else {
if (getConfig().hasPathOrNull("miner.minerCoinBase")) {
minerCoinBase = Hex.decode(getConfig().getString("miner.minerCoinBase"))
return minerCoinBase;
} else {
throw RuntimeException("Miner Coinbase is empty")
}
}
}

/**
* 设置矿工的Coinbase地址。
*/
fun setMinerCoinbase(newCoinbase: ByteArray) {
this.minerCoinBase = newCoinbase
}

/**
* Key-Value存储的实现。
*/
fun getDatabaseType(): String {
if (getConfig().hasPathOrNull("database.type")) {
return getConfig().getString("database.type")
} else {
return DATABASE_TYPE.MEMORY.name
}
}

/**
* 数据库的存储目录。
*/
fun getDatabaseDir(): String {
if (getConfig().hasPathOrNull("database.dir")) {
return getConfig().getString("database.dir")
} else {
return "database"
}
}

}

}
10 changes: 6 additions & 4 deletions src/main/kotlin/mbc/core/Account.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package mbc.core

import mbc.storage.Repository
import mbc.util.CryptoUtil
import java.math.BigInteger
import java.security.PublicKey

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

val balance: Long
get() = AccountState.getAccountBalance(address)
val balance: BigInteger
get() = Repository.getBalance(address)
}
29 changes: 12 additions & 17 deletions src/main/kotlin/mbc/core/AccountState.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
package mbc.core

import java.util.*
import mbc.util.CodecUtil
import java.math.BigInteger

/**
* 账户状态管理
* 账户状态
*/
object AccountState {
/**
* 账户余额存储,为了演示方便使用HashMap。
*/
val accountBalances: HashMap<String, Long> = HashMap()
class AccountState(val nonce: BigInteger, val balance: BigInteger) {

/**
* 根据账户地址(address)读取账户余额(balance)。
*/
fun getAccountBalance(address: String): Long {
return accountBalances.getOrDefault(address, 0)
fun encode(): ByteArray {
return CodecUtil.encodeAccountState(this)
}

/**
* 根据账户地址(address)更新账户余额(balance)。
*/
fun setAccountBalance(address: String, amount: Long): Long? {
return accountBalances.put(address, amount)
fun increaseNonce(): AccountState {
return AccountState(nonce + BigInteger.ONE, balance)
}


fun increaseBalance(amount: BigInteger): AccountState {
return AccountState(nonce, balance + amount)
}
}
21 changes: 9 additions & 12 deletions src/main/kotlin/mbc/core/Block.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
package mbc.core

import mbc.util.CodecUtil
import mbc.util.CryptoUtil
import org.joda.time.DateTime

/**
* 区块(Block)类,包含了区块高度(height),上一个区块哈希值(parentHash),旷工账户地址(minerAddress),交易列表(transactions)和时间戳(time)。
* 区块(Block)类,包含了区块高度(height),上一个区块哈希值(parentHash),旷工账户地址(coinBase),交易列表(transactions)和时间戳(time)。
*/
class Block(val height: Long, val parentHash: ByteArray, val minerAddress: String, val transactions: List<Transaction>,
val time: DateTime) {

val version: Int = 1

val merkleRoot: ByteArray
get() = CryptoUtil.merkleRoot(transactions)

var difficulty: Int = 0

var nonce: Int = 0
class Block(val version: Int, val height: Long, val parentHash: ByteArray, val merkleRoot: ByteArray,
val coinBase: ByteArray, val transactions: List<Transaction>, val time: DateTime, val difficulty: Int,
val nonce: Int) {

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

fun encode(): ByteArray {
return CodecUtil.encodeBlock(this)
}

}
14 changes: 9 additions & 5 deletions src/main/kotlin/mbc/core/BlockChain.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
package mbc.core

import mbc.core.TransactionExecutor.applyTrx
import mbc.config.BlockChainConfig
import mbc.core.TransactionExecutor.execute
import mbc.util.CryptoUtil
import org.joda.time.DateTime

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

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

for (trx in transactions) {
applyTrx(trx)
execute(trx)
}

return block
Expand Down
24 changes: 22 additions & 2 deletions src/main/kotlin/mbc/core/Transaction.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package mbc.core

import mbc.util.CodecUtil
import mbc.util.CryptoUtil
import org.joda.time.DateTime
import java.math.BigInteger
import java.security.PrivateKey
import java.security.PublicKey
import java.util.*

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

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

fun encode(): ByteArray {
return CodecUtil.encodeTransaction(this)
}

override fun equals(o: Any?): Boolean {
if (o is Transaction) {
if (!Arrays.equals(this.senderAddress, o.senderAddress)) return false
if (!Arrays.equals(this.receiverAddress, o.receiverAddress)) return false
if (this.amount != o.amount) return false
if (this.time != o.time) return false
if (this.publicKey != o.publicKey) return false

return true
} else {
return false
}
}
}
26 changes: 20 additions & 6 deletions src/main/kotlin/mbc/core/TransactionExecutor.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package mbc.core

import mbc.storage.Repository
import java.math.BigInteger

/**
* 交易处理并更新账户状态。
*/
Expand All @@ -8,19 +11,30 @@ object TransactionExecutor {
/**
* 增加账户余额(balance),如果amount为负数则余额减少。
*/
fun addAmount(address: String, amount: Long) {
val newBalance = AccountState.getAccountBalance(address) + amount
AccountState.setAccountBalance(address, newBalance)
fun addBalance(address: ByteArray, amount: BigInteger) {
Repository.addBalance(address, amount)
}

/**
* 转账功能,发送方减少金额,接收方增加金额。
*/
fun transfer(fromAddress: ByteArray, toAddress: ByteArray, amount: BigInteger) {
addBalance(fromAddress, amount.negate())
addBalance(toAddress, amount)
}

/**
* 根据交易记录更新区块链的状态(state),发送方的余额会减少,接收方的余额会增加。
* 区块链的状态更新应该是原子操作,持久层是数据库可以使用事务功能。
*/
fun applyTrx(trx: Transaction) {
fun execute(trx: Transaction) {
if (trx.isValid) {
addAmount(trx.senderAddress, -trx.amount)
addAmount(trx.receiverAddress, +trx.amount)

// 发送方的Nonce+1
Repository.increaseNonce(trx.senderAddress)

// 执行转账
transfer(trx.senderAddress, trx.receiverAddress, trx.amount)
} else {
throw IllegalTransactionException()
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/mbc/miner/BlockMiner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object BlockMiner {
val ver = block.version
val parentHash = block.parentHash
val merkleRoot = block.merkleRoot
val time = (block.time.millis/1000).toInt() // Current timestamp as seconds since 1970-01-01T00:00 UTC
val time = (block.time.millis / 1000).toInt() // Current timestamp as seconds since 1970-01-01T00:00 UTC
val difficulty = currentDifficulty // difficulty

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

0 comments on commit 00e44cf

Please sign in to comment.