Skip to content

Commit 6e27229

Browse files
committedFeb 4, 2017
Day 1. 账户与交易
1 parent 55e5a44 commit 6e27229

File tree

8 files changed

+213
-0
lines changed

8 files changed

+213
-0
lines changed
 

‎.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@
1010

1111
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
1212
hs_err_pid*
13+
14+
# idea project files
15+
.idea
16+
17+
# build files
18+
build

‎build.gradle

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
buildscript {
2+
ext.kotlin_version = '1.0.6'
3+
4+
repositories {
5+
mavenCentral()
6+
}
7+
8+
dependencies {
9+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
10+
}
11+
12+
}
13+
14+
apply plugin: "kotlin"
15+
apply plugin: "application"
16+
17+
mainClassName = 'demo.HelloWorldKt'
18+
19+
defaultTasks 'run'
20+
21+
repositories {
22+
mavenCentral()
23+
}
24+
25+
dependencies {
26+
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
27+
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'
32+
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
33+
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
34+
}
35+

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package mbc.core
2+
3+
import mbc.util.CryptoUtil
4+
import java.security.PublicKey
5+
6+
/**
7+
* 区块链账户类:记录账户地址(address)和余额(balance)信息。
8+
* 区块链账户由一个公私钥对唯一标识,构造Account对象需要公钥。
9+
* 账户地址(address)可以由公钥运算推导,比特币和以太坊均采用了类似机制。
10+
* 账户余额(balance)通常会保存在文件或数据库内。
11+
*/
12+
data class Account(val publicKey: PublicKey) {
13+
val address: String
14+
get() = CryptoUtil.generateAddress(publicKey)
15+
16+
val balance: Long
17+
get() = AccountState.getAccountBalance(address)
18+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package mbc.core
2+
3+
import java.util.*
4+
5+
/**
6+
* 账户状态管理。
7+
*/
8+
object AccountState {
9+
/**
10+
* 账户余额存储,为了演示方便使用HashMap。
11+
*/
12+
val accountBalances: HashMap<String, Long> = HashMap()
13+
14+
/**
15+
* 根据账户地址(address)读取账户余额(balance)。
16+
*/
17+
fun getAccountBalance(address: String): Long {
18+
return accountBalances.getOrDefault(address, 0)
19+
}
20+
21+
/**
22+
* 根据账户地址(address)更新账户余额(balance)。
23+
*/
24+
fun setAccountBalance(address: String, amount: Long): Long? {
25+
return accountBalances.put(address, amount)
26+
}
27+
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package mbc.core
2+
3+
import org.joda.time.DateTime
4+
5+
/**
6+
* 交易记录类:记录了发送方(sender)向接受方(receiver)的转账记录,包括金额(amount)和时间戳(time)。
7+
* 为简化模型,没有加入费用(fee)。
8+
*/
9+
data class Transaction(val senderAddress: String, val receiverAddress: String, val amount: Long,
10+
val time: DateTime)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package mbc.core
2+
3+
/**
4+
* 交易处理并更新账户状态。
5+
*/
6+
object TransactionExecutor {
7+
8+
/**
9+
* 增加账户余额(balance),如果amount为负数则余额减少。
10+
*/
11+
fun addAmount(address: String, amount: Long) {
12+
val newBalance = AccountState.getAccountBalance(address) + amount
13+
AccountState.setAccountBalance(address, newBalance)
14+
}
15+
16+
/**
17+
* 根据交易记录更新区块链的状态(state),发送方的余额会减少,接收方的余额会增加。
18+
* 区块链的状态更新应该是原子操作,持久层是数据库可以使用事务功能。
19+
*/
20+
fun applyTrx(trx: Transaction) {
21+
addAmount(trx.senderAddress, -trx.amount)
22+
addAmount(trx.receiverAddress, +trx.amount)
23+
}
24+
25+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package mbc.util
2+
3+
import org.spongycastle.jce.provider.BouncyCastleProvider
4+
import org.spongycastle.util.encoders.Hex
5+
import java.security.*
6+
import java.security.Security.insertProviderAt
7+
import java.security.spec.ECGenParameterSpec
8+
9+
/**
10+
* 密码学工具类。
11+
*/
12+
class CryptoUtil {
13+
14+
companion object {
15+
init {
16+
insertProviderAt(BouncyCastleProvider(), 1)
17+
}
18+
19+
/**
20+
* 根据公钥(public key)推算出账户地址,使用以太坊的算法,先KECCAK-256计算哈希值(32位),取后20位作为账户地址。
21+
* 比特币地址算法:http://www.infoq.com/cn/articles/bitcoin-and-block-chain-part03
22+
* 以太坊地址算法:http://ethereum.stackexchange.com/questions/3542/how-are-ethereum-addresses-generated
23+
*/
24+
fun generateAddress(publicKey: PublicKey): String {
25+
val digest = MessageDigest.getInstance("KECCAK-256", "SC")
26+
digest.update(publicKey.encoded)
27+
val hash = digest.digest()
28+
29+
return Hex.toHexString(hash.drop(12).toByteArray())
30+
}
31+
32+
/**
33+
* 生成公私钥对,使用以太坊的ECDSA算法(secp256k1)。
34+
*/
35+
fun generateKeyPair(): KeyPair? {
36+
val gen = KeyPairGenerator.getInstance("EC", "SC")
37+
gen.initialize(ECGenParameterSpec("secp256k1"), SecureRandom())
38+
val keyPair = gen.generateKeyPair()
39+
return keyPair
40+
}
41+
}
42+
43+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package mbc.core
2+
3+
import mbc.core.TransactionExecutor.addAmount
4+
import mbc.core.TransactionExecutor.applyTrx
5+
import mbc.util.CryptoUtil.Companion.generateKeyPair
6+
import org.joda.time.DateTime
7+
import org.junit.Test
8+
9+
class BlockChainTest {
10+
11+
/**
12+
* 验证账户地址长度为40(20个byte)。
13+
*/
14+
@Test fun validateAddress() {
15+
val keyPair = generateKeyPair() ?: return
16+
17+
val account = Account(keyPair.public)
18+
assert(account.address.length == 40)
19+
}
20+
21+
/**
22+
* 验证交易完成后账户余额(balance)是否正确。
23+
*/
24+
@Test fun applyTransaction() {
25+
// 初始化Alice账户
26+
val kp1 = generateKeyPair() ?: return
27+
val alice = Account(kp1.public)
28+
29+
// 初始化Bob账户
30+
val kp2 = generateKeyPair() ?: return
31+
val bob = Account(kp2.public)
32+
33+
// 初始金额为200
34+
addAmount(alice.address, 200)
35+
addAmount(bob.address, 200)
36+
37+
// Alice向Bob转账100
38+
val trx = Transaction(alice.address, bob.address, 100, DateTime())
39+
40+
// 根据交易记录更新区块链状态
41+
applyTrx(trx)
42+
43+
// 查询余额是否正确
44+
assert(alice.balance == 100L)
45+
assert(bob.balance == 300L)
46+
}
47+
48+
}

0 commit comments

Comments
 (0)
Please sign in to comment.