mirror of
https://github.com/number571/blockchain.git
synced 2026-09-12 19:50:00 +05:00
delete older code
This commit is contained in:
parent
5c6458a9d4
commit
56cb813379
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
main
|
||||
mainbc
|
||||
mainnt
|
||||
node
|
||||
client
|
||||
node1.key
|
||||
|
||||
@ -1,692 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"time"
|
||||
"errors"
|
||||
"bytes"
|
||||
"sort"
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"os"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
)
|
||||
|
||||
type BlockChain struct {
|
||||
index uint64
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
Nonce uint64
|
||||
Difficulty uint8
|
||||
CurrHash []byte
|
||||
PrevHash []byte
|
||||
Transactions []Transaction
|
||||
Mapping map[string]uint64
|
||||
Miner string
|
||||
Signature []byte
|
||||
TimeStamp string
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
RandBytes []byte
|
||||
PrevBlock []byte
|
||||
Sender string
|
||||
Receiver string
|
||||
Value uint64
|
||||
ToStorage uint64
|
||||
CurrHash []byte
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
const (
|
||||
CREATE_TABLE = `
|
||||
CREATE TABLE BlockChain (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
Hash VARCHAR(44) UNIQUE,
|
||||
Block TEXT
|
||||
);
|
||||
`
|
||||
)
|
||||
|
||||
const (
|
||||
GENESIS_BLOCK = "GENESIS-BLOCK"
|
||||
STORAGE_VALUE = 100
|
||||
GENESIS_REWARD = 100
|
||||
STORAGE_CHAIN = "STORAGE-CHAIN"
|
||||
)
|
||||
|
||||
const (
|
||||
DIFFICULTY = 20
|
||||
)
|
||||
|
||||
const (
|
||||
RAND_BYTES = 32
|
||||
START_PERCENT = 10
|
||||
STORAGE_REWARD = 1
|
||||
)
|
||||
|
||||
const (
|
||||
TXS_LIMIT = 2
|
||||
)
|
||||
|
||||
const (
|
||||
DEBUG = true
|
||||
)
|
||||
|
||||
const (
|
||||
KEY_SIZE = 512
|
||||
)
|
||||
|
||||
func NewChain(filename, receiver string) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
db, err := sql.Open("sqlite3", filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
_, err = db.Exec(CREATE_TABLE)
|
||||
chain := &BlockChain{
|
||||
DB: db,
|
||||
}
|
||||
genesis := &Block{
|
||||
CurrHash: []byte(GENESIS_BLOCK),
|
||||
Mapping: make(map[string]uint64),
|
||||
Miner: receiver,
|
||||
TimeStamp: time.Now().Format(time.RFC3339),
|
||||
}
|
||||
genesis.Mapping[STORAGE_CHAIN] = STORAGE_VALUE
|
||||
genesis.Mapping[receiver] = GENESIS_REWARD
|
||||
chain.AddBlock(genesis)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (chain *BlockChain) AddBlock(block *Block) {
|
||||
chain.index += 1
|
||||
chain.DB.Exec("INSERT INTO BlockChain (Hash, Block) VALUES ($1, $2)",
|
||||
Base64Encode(block.CurrHash),
|
||||
SerializeBlock(block),
|
||||
)
|
||||
}
|
||||
|
||||
func SerializeBlock(block *Block) string {
|
||||
jsonData, err := json.MarshalIndent(*block, "", "\t")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
func LoadChain(filename string) *BlockChain {
|
||||
db, err := sql.Open("sqlite3", filename)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
chain := &BlockChain{
|
||||
DB: db,
|
||||
}
|
||||
chain.index = chain.Size()
|
||||
return chain
|
||||
}
|
||||
|
||||
func (chain *BlockChain) Size() uint64 {
|
||||
var index uint64
|
||||
row := chain.DB.QueryRow("SELECT Id FROM BlockChain ORDER BY Id DESC")
|
||||
row.Scan(&index)
|
||||
return index
|
||||
}
|
||||
|
||||
func NewBlock(miner string, prevHash []byte) *Block {
|
||||
return &Block{
|
||||
Difficulty: DIFFICULTY,
|
||||
PrevHash: prevHash,
|
||||
Miner: miner,
|
||||
Mapping: make(map[string]uint64),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTransaction(user *User, lasthash []byte, to string, value uint64) *Transaction {
|
||||
tx := &Transaction{
|
||||
RandBytes: GenerateRandomBytes(RAND_BYTES),
|
||||
PrevBlock: lasthash,
|
||||
Sender: user.Address(),
|
||||
Receiver: to,
|
||||
Value: value,
|
||||
}
|
||||
if value > START_PERCENT {
|
||||
tx.ToStorage = STORAGE_REWARD
|
||||
}
|
||||
tx.CurrHash = tx.hash()
|
||||
tx.Signature = tx.sign(user.Private())
|
||||
return tx
|
||||
}
|
||||
|
||||
type User struct {
|
||||
PrivateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func GenerateRandomBytes(max uint) []byte {
|
||||
var slice []byte = make([]byte, max)
|
||||
_, err := rand.Read(slice)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
func (user *User) Address() string {
|
||||
return StringPublic(user.Public())
|
||||
}
|
||||
|
||||
func (user *User) Private() *rsa.PrivateKey {
|
||||
return user.PrivateKey
|
||||
}
|
||||
|
||||
func (tx *Transaction) hash() []byte {
|
||||
return HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
tx.RandBytes,
|
||||
tx.PrevBlock,
|
||||
[]byte(tx.Sender),
|
||||
[]byte(tx.Receiver),
|
||||
ToBytes(tx.Value),
|
||||
ToBytes(tx.ToStorage),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
}
|
||||
|
||||
func (tx *Transaction) sign(priv *rsa.PrivateKey) []byte {
|
||||
return Sign(priv, tx.CurrHash)
|
||||
}
|
||||
|
||||
func StringPublic(pub *rsa.PublicKey) string {
|
||||
return Base64Encode(x509.MarshalPKCS1PublicKey(pub))
|
||||
}
|
||||
|
||||
func (user *User) Public() *rsa.PublicKey {
|
||||
return &(user.PrivateKey).PublicKey
|
||||
}
|
||||
|
||||
func HashSum(data []byte) []byte {
|
||||
hash := sha256.Sum256(data)
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
func ToBytes(num uint64) []byte {
|
||||
var data = new(bytes.Buffer)
|
||||
err := binary.Write(data, binary.BigEndian, num)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return data.Bytes()
|
||||
}
|
||||
|
||||
func Sign(priv *rsa.PrivateKey, data []byte) []byte {
|
||||
signature, err := rsa.SignPSS(rand.Reader, priv, crypto.SHA256, data, nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return signature
|
||||
}
|
||||
|
||||
func Base64Encode(data []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
func (block *Block) AddTransaction(chain *BlockChain, tx *Transaction) error {
|
||||
if tx == nil {
|
||||
return errors.New("tx is null")
|
||||
}
|
||||
if tx.Value == 0 {
|
||||
return errors.New("tx value = 0")
|
||||
}
|
||||
if len(block.Transactions) == TXS_LIMIT && tx.Sender != STORAGE_CHAIN {
|
||||
return errors.New("len tx = limit")
|
||||
}
|
||||
var balanceInChain uint64
|
||||
balanceInTX := tx.Value + tx.ToStorage
|
||||
if value, ok := block.Mapping[tx.Sender]; ok {
|
||||
balanceInChain = value
|
||||
} else {
|
||||
balanceInChain = chain.Balance(tx.Sender)
|
||||
}
|
||||
if tx.Value > START_PERCENT && tx.ToStorage != STORAGE_REWARD {
|
||||
return errors.New("storage reward pass")
|
||||
}
|
||||
if balanceInTX > balanceInChain {
|
||||
return errors.New("insufficient funds")
|
||||
}
|
||||
block.Mapping[tx.Sender] = balanceInChain - balanceInTX
|
||||
block.addBalance(chain, tx.Receiver, tx.Value)
|
||||
block.addBalance(chain, STORAGE_CHAIN, tx.ToStorage)
|
||||
block.Transactions = append(block.Transactions, *tx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (chain *BlockChain) Balance(address string) uint64 {
|
||||
var (
|
||||
sblock string
|
||||
block *Block
|
||||
balance uint64
|
||||
)
|
||||
rows, err := chain.DB.Query("SELECT Block FROM BlockChain WHERE Id <= $1 ORDER BY Id DESC",
|
||||
chain.index)
|
||||
if err != nil {
|
||||
return balance
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
rows.Scan(&sblock)
|
||||
block = DeserializeBlock(sblock)
|
||||
if value, ok := block.Mapping[address]; ok {
|
||||
balance = value
|
||||
break
|
||||
}
|
||||
}
|
||||
return balance
|
||||
}
|
||||
|
||||
func (block *Block) addBalance(chain *BlockChain, receiver string, value uint64) {
|
||||
var balanceInChain uint64
|
||||
if v, ok := block.Mapping[receiver]; ok {
|
||||
balanceInChain = v
|
||||
} else {
|
||||
balanceInChain = chain.Balance(receiver)
|
||||
}
|
||||
block.Mapping[receiver] = balanceInChain + value
|
||||
}
|
||||
|
||||
func DeserializeBlock(data string) *Block {
|
||||
var block Block
|
||||
err := json.Unmarshal([]byte(data), &block)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &block
|
||||
}
|
||||
|
||||
func (block *Block) Accept(chain *BlockChain, user *User, ch chan bool) error {
|
||||
if !block.transactionsIsValid(chain) {
|
||||
return errors.New("transactions is not valid")
|
||||
}
|
||||
block.AddTransaction(chain, &Transaction{
|
||||
RandBytes: GenerateRandomBytes(RAND_BYTES),
|
||||
Sender: STORAGE_CHAIN,
|
||||
Receiver: user.Address(),
|
||||
Value: STORAGE_REWARD,
|
||||
})
|
||||
block.TimeStamp = time.Now().Format(time.RFC3339)
|
||||
block.CurrHash = block.hash()
|
||||
block.Signature = block.sign(user.Private())
|
||||
block.Nonce = block.proof(ch)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (block *Block) transactionsIsValid(chain *BlockChain) bool {
|
||||
lentxs := len(block.Transactions)
|
||||
plusStorage := 0
|
||||
for i := 0; i < lentxs; i++ {
|
||||
if block.Transactions[i].Sender == STORAGE_CHAIN {
|
||||
plusStorage = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
if lentxs == 0 || lentxs > TXS_LIMIT+plusStorage {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < lentxs-1; i++ {
|
||||
for j := i + 1; j < lentxs; j++ {
|
||||
if bytes.Equal(block.Transactions[i].RandBytes, block.Transactions[j].RandBytes) {
|
||||
return false
|
||||
}
|
||||
if block.Transactions[i].Sender == STORAGE_CHAIN &&
|
||||
block.Transactions[j].Sender == STORAGE_CHAIN {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < lentxs; i++ {
|
||||
tx := block.Transactions[i]
|
||||
if tx.Sender == STORAGE_CHAIN {
|
||||
if tx.Receiver != block.Miner || tx.Value != STORAGE_REWARD {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !tx.hashIsValid() {
|
||||
return false
|
||||
}
|
||||
if !tx.signIsValid() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !block.balanceIsValid(chain, tx.Sender) {
|
||||
return false
|
||||
}
|
||||
if !block.balanceIsValid(chain, tx.Receiver) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (block *Block) hash() []byte {
|
||||
var tempHash []byte
|
||||
for _, tx := range block.Transactions {
|
||||
tempHash = HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
tempHash,
|
||||
tx.CurrHash,
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
}
|
||||
var list []string
|
||||
for hash := range block.Mapping {
|
||||
list = append(list, hash)
|
||||
}
|
||||
sort.Strings(list)
|
||||
for _, hash := range list {
|
||||
tempHash = HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
tempHash,
|
||||
[]byte(hash),
|
||||
ToBytes(block.Mapping[hash]),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
}
|
||||
return HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
tempHash,
|
||||
ToBytes(uint64(block.Difficulty)),
|
||||
block.PrevHash,
|
||||
[]byte(block.Miner),
|
||||
[]byte(block.TimeStamp),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
}
|
||||
|
||||
func (block *Block) sign(priv *rsa.PrivateKey) []byte {
|
||||
return Sign(priv, block.CurrHash)
|
||||
}
|
||||
|
||||
func (block *Block) proof(ch chan bool) uint64 {
|
||||
return ProofOfWork(block.CurrHash, block.Difficulty, ch)
|
||||
}
|
||||
|
||||
func (tx *Transaction) hashIsValid() bool {
|
||||
return bytes.Equal(tx.hash(), tx.CurrHash)
|
||||
}
|
||||
|
||||
func (tx *Transaction) signIsValid() bool {
|
||||
return Verify(ParsePublic(tx.Sender), tx.CurrHash, tx.Signature) == nil
|
||||
}
|
||||
|
||||
func (block *Block) balanceIsValid(chain *BlockChain, address string) bool {
|
||||
if _, ok := block.Mapping[address]; !ok {
|
||||
return false
|
||||
}
|
||||
lentxs := len(block.Transactions)
|
||||
balanceInChain := chain.Balance(address)
|
||||
balanceSubBlock := uint64(0)
|
||||
balanceAddBlock := uint64(0)
|
||||
for j := 0; j < lentxs; j++ {
|
||||
tx := block.Transactions[j]
|
||||
if tx.Sender == address {
|
||||
balanceSubBlock += tx.Value + tx.ToStorage
|
||||
}
|
||||
if tx.Receiver == address {
|
||||
balanceAddBlock += tx.Value
|
||||
}
|
||||
if STORAGE_CHAIN == address {
|
||||
balanceAddBlock += tx.ToStorage
|
||||
}
|
||||
}
|
||||
if (balanceInChain + balanceAddBlock - balanceSubBlock) != block.Mapping[address] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ProofOfWork(blockHash []byte, difficulty uint8, ch chan bool) uint64 {
|
||||
var (
|
||||
Target = big.NewInt(1)
|
||||
intHash = big.NewInt(1)
|
||||
nonce = uint64(mrand.Intn(math.MaxUint32))
|
||||
hash []byte
|
||||
)
|
||||
Target.Lsh(Target, 256 - uint(difficulty))
|
||||
for nonce < math.MaxUint64 {
|
||||
select {
|
||||
case <-ch:
|
||||
if DEBUG {
|
||||
fmt.Println()
|
||||
}
|
||||
return nonce
|
||||
default:
|
||||
hash = HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
blockHash,
|
||||
ToBytes(nonce),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
if DEBUG {
|
||||
fmt.Printf("\rMining: %s", Base64Encode(hash))
|
||||
}
|
||||
intHash.SetBytes(hash)
|
||||
if intHash.Cmp(Target) == -1 {
|
||||
if DEBUG {
|
||||
fmt.Println()
|
||||
}
|
||||
return nonce
|
||||
}
|
||||
nonce++
|
||||
}
|
||||
}
|
||||
return nonce
|
||||
}
|
||||
|
||||
func Verify(pub *rsa.PublicKey, data, sign []byte) error {
|
||||
return rsa.VerifyPSS(pub, crypto.SHA256, data, sign, nil)
|
||||
}
|
||||
|
||||
func ParsePublic(pubData string) *rsa.PublicKey {
|
||||
pub, err := x509.ParsePKCS1PublicKey(Base64Decode(pubData))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return pub
|
||||
}
|
||||
|
||||
func init() {
|
||||
mrand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func Base64Decode(data string) []byte {
|
||||
result, err := base64.StdEncoding.DecodeString(data)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func GeneratePrivate(bits uint) *rsa.PrivateKey {
|
||||
priv, err := rsa.GenerateKey(rand.Reader, int(bits))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return priv
|
||||
}
|
||||
|
||||
func StringPrivate(priv *rsa.PrivateKey) string {
|
||||
return Base64Encode(x509.MarshalPKCS1PrivateKey(priv))
|
||||
}
|
||||
|
||||
func ParsePrivate(privData string) *rsa.PrivateKey {
|
||||
pub, err := x509.ParsePKCS1PrivateKey(Base64Decode(privData))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return pub
|
||||
}
|
||||
|
||||
func NewUser() *User {
|
||||
return &User{
|
||||
PrivateKey: GeneratePrivate(KEY_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
func LoadUser(purse string) *User {
|
||||
priv := ParsePrivate(purse)
|
||||
if priv == nil {
|
||||
return nil
|
||||
}
|
||||
return &User{
|
||||
PrivateKey: priv,
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) Purse() string {
|
||||
return StringPrivate(user.Private())
|
||||
}
|
||||
|
||||
func (chain *BlockChain) LastHash() []byte {
|
||||
var hash string
|
||||
row := chain.DB.QueryRow("SELECT Hash FROM BlockChain ORDER BY Id DESC")
|
||||
row.Scan(&hash)
|
||||
return Base64Decode(hash)
|
||||
}
|
||||
|
||||
func (block *Block) IsValid(chain *BlockChain) bool {
|
||||
switch {
|
||||
case block == nil:
|
||||
return false
|
||||
case block.Difficulty != DIFFICULTY:
|
||||
return false
|
||||
case !block.hashIsValid(chain, chain.Size()):
|
||||
return false
|
||||
case !block.signIsValid():
|
||||
return false
|
||||
case !block.proofIsValid():
|
||||
return false
|
||||
case !block.mappingIsValid():
|
||||
return false
|
||||
case !block.timeIsValid(chain, chain.Size()):
|
||||
return false
|
||||
case !block.transactionsIsValid(chain):
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func SerializeTX(tx *Transaction) string {
|
||||
jsonData, err := json.MarshalIndent(*tx, "", "\t")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
func DeserializeTX(data string) *Transaction {
|
||||
var tx Transaction
|
||||
err := json.Unmarshal([]byte(data), &tx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &tx
|
||||
}
|
||||
|
||||
func (block *Block) hashIsValid(chain *BlockChain, index uint64) bool {
|
||||
if !bytes.Equal(block.hash(), block.CurrHash) {
|
||||
return false
|
||||
}
|
||||
var id uint64
|
||||
row := chain.DB.QueryRow("SELECT Id FROM BlockChain WHERE Hash=$1",
|
||||
Base64Encode(block.PrevHash))
|
||||
row.Scan(&id)
|
||||
return id == index
|
||||
}
|
||||
|
||||
func (block *Block) signIsValid() bool {
|
||||
return Verify(ParsePublic(block.Miner), block.CurrHash, block.Signature) == nil
|
||||
}
|
||||
|
||||
func (block *Block) proofIsValid() bool {
|
||||
intHash := big.NewInt(1)
|
||||
Target := big.NewInt(1)
|
||||
hash := HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
block.CurrHash,
|
||||
ToBytes(block.Nonce),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
intHash.SetBytes(hash)
|
||||
Target.Lsh(Target, 256 - uint(block.Difficulty))
|
||||
if intHash.Cmp(Target) == -1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (block *Block) mappingIsValid() bool {
|
||||
for hash := range block.Mapping {
|
||||
if hash == STORAGE_CHAIN {
|
||||
continue
|
||||
}
|
||||
flag := false
|
||||
for _, tx := range block.Transactions {
|
||||
if tx.Sender == hash || tx.Receiver == hash {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (block *Block) timeIsValid(chain *BlockChain, index uint64) bool {
|
||||
btime, err := time.Parse(time.RFC3339, block.TimeStamp)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
diff := time.Now().Sub(btime)
|
||||
if diff < 0 {
|
||||
return false
|
||||
}
|
||||
var sblock string
|
||||
row := chain.DB.QueryRow("SELECT Block FROM BlockChain WHERE Hash=$1",
|
||||
Base64Encode(block.PrevHash))
|
||||
row.Scan(&sblock)
|
||||
lblock := DeserializeBlock(sblock)
|
||||
if lblock == nil {
|
||||
return false
|
||||
}
|
||||
ltime, err := time.Parse(time.RFC3339, lblock.TimeStamp)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
result := btime.Sub(ltime)
|
||||
return result > 0
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
bc "./blockchain"
|
||||
)
|
||||
|
||||
const (
|
||||
DBNAME = "blockchain.db"
|
||||
)
|
||||
|
||||
func main() {
|
||||
miner := bc.NewUser()
|
||||
bc.NewChain(DBNAME, miner.Address())
|
||||
|
||||
chain := bc.LoadChain(DBNAME)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
block := bc.NewBlock(miner.Address(), chain.LastHash())
|
||||
|
||||
block.AddTransaction(chain,
|
||||
bc.NewTransaction(miner, chain.LastHash(), "aaa", 5))
|
||||
block.AddTransaction(chain,
|
||||
bc.NewTransaction(miner, chain.LastHash(), "bbb", 3))
|
||||
|
||||
block.Accept(chain, miner, make(chan bool))
|
||||
chain.AddBlock(block)
|
||||
}
|
||||
|
||||
var sblock string
|
||||
rows, err := chain.DB.Query("SELECT Block FROM BlockChain")
|
||||
if err != nil {
|
||||
panic("error: query to db")
|
||||
}
|
||||
for rows.Next() {
|
||||
rows.Scan(&sblock)
|
||||
fmt.Println(sblock)
|
||||
}
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"strings"
|
||||
nt "./network"
|
||||
)
|
||||
|
||||
const (
|
||||
TO_UPPER = iota + 1
|
||||
TO_LOWER
|
||||
)
|
||||
|
||||
const (
|
||||
ADDRESS = ":8080"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
res = new(nt.Package)
|
||||
msg = "Hello, World!"
|
||||
)
|
||||
|
||||
go nt.Listen(ADDRESS, handleServer)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
res = nt.Send(ADDRESS, &nt.Package{
|
||||
Option: TO_UPPER,
|
||||
Data: msg,
|
||||
})
|
||||
fmt.Println(res.Data)
|
||||
|
||||
res = nt.Send(ADDRESS, &nt.Package{
|
||||
Option: TO_LOWER,
|
||||
Data: res.Data,
|
||||
})
|
||||
fmt.Println(res.Data)
|
||||
}
|
||||
|
||||
func handleServer(conn nt.Conn, pack *nt.Package) {
|
||||
nt.Handle(TO_UPPER, conn, pack, handleToUpper)
|
||||
nt.Handle(TO_LOWER, conn, pack, handleToLower)
|
||||
}
|
||||
|
||||
func handleToUpper(pack *nt.Package) string {
|
||||
return strings.ToUpper(pack.Data)
|
||||
}
|
||||
|
||||
func handleToLower(pack *nt.Package) string {
|
||||
return strings.ToLower(pack.Data)
|
||||
}
|
||||
@ -1,130 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Package struct {
|
||||
Option int
|
||||
Data string
|
||||
}
|
||||
|
||||
func Send(address string, pack *Package) *Package {
|
||||
conn, err := net.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
conn.Write([]byte(SerializePackage(pack) + ENDBYTES))
|
||||
var res = new(Package)
|
||||
ch := make(chan bool)
|
||||
go func() {
|
||||
res = readPackage(conn)
|
||||
ch <- true
|
||||
}()
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(WAITTIME * time.Second):
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func SerializePackage(pack *Package) string {
|
||||
jsonData, err := json.MarshalIndent(*pack, "", "\t")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
const (
|
||||
ENDBYTES = "\000\005\007\001\001\007\005\000"
|
||||
)
|
||||
|
||||
func readPackage(conn net.Conn) *Package {
|
||||
var (
|
||||
data string
|
||||
size = uint64(0)
|
||||
buffer = make([]byte, BUFFSIZE)
|
||||
)
|
||||
for {
|
||||
length, err := conn.Read(buffer)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
size += uint64(length)
|
||||
if size > DMAXSIZE {
|
||||
return nil
|
||||
}
|
||||
data += string(buffer[:length])
|
||||
if strings.Contains(data, ENDBYTES) {
|
||||
data = strings.Split(data, ENDBYTES)[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
return DeserializePackage(data)
|
||||
}
|
||||
|
||||
const (
|
||||
WAITTIME = 5 // seconds
|
||||
DMAXSIZE = (2 << 20) // (2^20)*2 = 2MiB
|
||||
BUFFSIZE = (4 << 10) // (2^10)*4 = 4KiB
|
||||
)
|
||||
|
||||
func DeserializePackage(data string) *Package {
|
||||
var pack Package
|
||||
err := json.Unmarshal([]byte(data), &pack)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &pack
|
||||
}
|
||||
|
||||
func Listen(address string, handle func(Conn, *Package)) Listener {
|
||||
splited := strings.Split(address, ":")
|
||||
if len(splited) != 2 {
|
||||
return nil
|
||||
}
|
||||
listener, err := net.Listen("tcp", "0.0.0.0:"+splited[1])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
go serve(listener, handle)
|
||||
return Listener(listener)
|
||||
}
|
||||
|
||||
func serve(listener net.Listener, handle func(Conn, *Package)) {
|
||||
defer listener.Close()
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
go handleConn(conn, handle)
|
||||
}
|
||||
}
|
||||
func handleConn(conn net.Conn, handle func(Conn, *Package)) {
|
||||
defer conn.Close()
|
||||
pack := readPackage(conn)
|
||||
if pack == nil {
|
||||
return
|
||||
}
|
||||
handle(Conn(conn), pack)
|
||||
}
|
||||
|
||||
type Listener net.Listener
|
||||
type Conn net.Conn
|
||||
|
||||
func Handle(option int, conn Conn, pack *Package, handle func(*Package) string) bool {
|
||||
if pack.Option != option {
|
||||
return false
|
||||
}
|
||||
conn.Write([]byte(SerializePackage(&Package{
|
||||
Option: option,
|
||||
Data: handle(pack),
|
||||
}) + ENDBYTES))
|
||||
return true
|
||||
}
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
[
|
||||
":8080",
|
||||
":9090"
|
||||
]
|
||||
@ -1,692 +0,0 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"time"
|
||||
"errors"
|
||||
"bytes"
|
||||
"sort"
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"os"
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
)
|
||||
|
||||
type BlockChain struct {
|
||||
index uint64
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
Nonce uint64
|
||||
Difficulty uint8
|
||||
CurrHash []byte
|
||||
PrevHash []byte
|
||||
Transactions []Transaction
|
||||
Mapping map[string]uint64
|
||||
Miner string
|
||||
Signature []byte
|
||||
TimeStamp string
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
RandBytes []byte
|
||||
PrevBlock []byte
|
||||
Sender string
|
||||
Receiver string
|
||||
Value uint64
|
||||
ToStorage uint64
|
||||
CurrHash []byte
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
const (
|
||||
CREATE_TABLE = `
|
||||
CREATE TABLE BlockChain (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
Hash VARCHAR(44) UNIQUE,
|
||||
Block TEXT
|
||||
);
|
||||
`
|
||||
)
|
||||
|
||||
const (
|
||||
GENESIS_BLOCK = "GENESIS-BLOCK"
|
||||
STORAGE_VALUE = 100
|
||||
GENESIS_REWARD = 100
|
||||
STORAGE_CHAIN = "STORAGE-CHAIN"
|
||||
)
|
||||
|
||||
const (
|
||||
DIFFICULTY = 20
|
||||
)
|
||||
|
||||
const (
|
||||
RAND_BYTES = 32
|
||||
START_PERCENT = 10
|
||||
STORAGE_REWARD = 1
|
||||
)
|
||||
|
||||
const (
|
||||
TXS_LIMIT = 2
|
||||
)
|
||||
|
||||
const (
|
||||
DEBUG = true
|
||||
)
|
||||
|
||||
const (
|
||||
KEY_SIZE = 512
|
||||
)
|
||||
|
||||
func NewChain(filename, receiver string) error {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
db, err := sql.Open("sqlite3", filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close()
|
||||
_, err = db.Exec(CREATE_TABLE)
|
||||
chain := &BlockChain{
|
||||
DB: db,
|
||||
}
|
||||
genesis := &Block{
|
||||
CurrHash: []byte(GENESIS_BLOCK),
|
||||
Mapping: make(map[string]uint64),
|
||||
Miner: receiver,
|
||||
TimeStamp: time.Now().Format(time.RFC3339),
|
||||
}
|
||||
genesis.Mapping[STORAGE_CHAIN] = STORAGE_VALUE
|
||||
genesis.Mapping[receiver] = GENESIS_REWARD
|
||||
chain.AddBlock(genesis)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (chain *BlockChain) AddBlock(block *Block) {
|
||||
chain.index += 1
|
||||
chain.DB.Exec("INSERT INTO BlockChain (Hash, Block) VALUES ($1, $2)",
|
||||
Base64Encode(block.CurrHash),
|
||||
SerializeBlock(block),
|
||||
)
|
||||
}
|
||||
|
||||
func SerializeBlock(block *Block) string {
|
||||
jsonData, err := json.MarshalIndent(*block, "", "\t")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
func LoadChain(filename string) *BlockChain {
|
||||
db, err := sql.Open("sqlite3", filename)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
chain := &BlockChain{
|
||||
DB: db,
|
||||
}
|
||||
chain.index = chain.Size()
|
||||
return chain
|
||||
}
|
||||
|
||||
func (chain *BlockChain) Size() uint64 {
|
||||
var index uint64
|
||||
row := chain.DB.QueryRow("SELECT Id FROM BlockChain ORDER BY Id DESC")
|
||||
row.Scan(&index)
|
||||
return index
|
||||
}
|
||||
|
||||
func NewBlock(miner string, prevHash []byte) *Block {
|
||||
return &Block{
|
||||
Difficulty: DIFFICULTY,
|
||||
PrevHash: prevHash,
|
||||
Miner: miner,
|
||||
Mapping: make(map[string]uint64),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTransaction(user *User, lasthash []byte, to string, value uint64) *Transaction {
|
||||
tx := &Transaction{
|
||||
RandBytes: GenerateRandomBytes(RAND_BYTES),
|
||||
PrevBlock: lasthash,
|
||||
Sender: user.Address(),
|
||||
Receiver: to,
|
||||
Value: value,
|
||||
}
|
||||
if value > START_PERCENT {
|
||||
tx.ToStorage = STORAGE_REWARD
|
||||
}
|
||||
tx.CurrHash = tx.hash()
|
||||
tx.Signature = tx.sign(user.Private())
|
||||
return tx
|
||||
}
|
||||
|
||||
type User struct {
|
||||
PrivateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
func GenerateRandomBytes(max uint) []byte {
|
||||
var slice []byte = make([]byte, max)
|
||||
_, err := rand.Read(slice)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
||||
func (user *User) Address() string {
|
||||
return StringPublic(user.Public())
|
||||
}
|
||||
|
||||
func (user *User) Private() *rsa.PrivateKey {
|
||||
return user.PrivateKey
|
||||
}
|
||||
|
||||
func (tx *Transaction) hash() []byte {
|
||||
return HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
tx.RandBytes,
|
||||
tx.PrevBlock,
|
||||
[]byte(tx.Sender),
|
||||
[]byte(tx.Receiver),
|
||||
ToBytes(tx.Value),
|
||||
ToBytes(tx.ToStorage),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
}
|
||||
|
||||
func (tx *Transaction) sign(priv *rsa.PrivateKey) []byte {
|
||||
return Sign(priv, tx.CurrHash)
|
||||
}
|
||||
|
||||
func StringPublic(pub *rsa.PublicKey) string {
|
||||
return Base64Encode(x509.MarshalPKCS1PublicKey(pub))
|
||||
}
|
||||
|
||||
func (user *User) Public() *rsa.PublicKey {
|
||||
return &(user.PrivateKey).PublicKey
|
||||
}
|
||||
|
||||
func HashSum(data []byte) []byte {
|
||||
hash := sha256.Sum256(data)
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
func ToBytes(num uint64) []byte {
|
||||
var data = new(bytes.Buffer)
|
||||
err := binary.Write(data, binary.BigEndian, num)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return data.Bytes()
|
||||
}
|
||||
|
||||
func Sign(priv *rsa.PrivateKey, data []byte) []byte {
|
||||
signature, err := rsa.SignPSS(rand.Reader, priv, crypto.SHA256, data, nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return signature
|
||||
}
|
||||
|
||||
func Base64Encode(data []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
func (block *Block) AddTransaction(chain *BlockChain, tx *Transaction) error {
|
||||
if tx == nil {
|
||||
return errors.New("tx is null")
|
||||
}
|
||||
if tx.Value == 0 {
|
||||
return errors.New("tx value = 0")
|
||||
}
|
||||
if len(block.Transactions) == TXS_LIMIT && tx.Sender != STORAGE_CHAIN {
|
||||
return errors.New("len tx = limit")
|
||||
}
|
||||
var balanceInChain uint64
|
||||
balanceInTX := tx.Value + tx.ToStorage
|
||||
if value, ok := block.Mapping[tx.Sender]; ok {
|
||||
balanceInChain = value
|
||||
} else {
|
||||
balanceInChain = chain.Balance(tx.Sender)
|
||||
}
|
||||
if tx.Value > START_PERCENT && tx.ToStorage != STORAGE_REWARD {
|
||||
return errors.New("storage reward pass")
|
||||
}
|
||||
if balanceInTX > balanceInChain {
|
||||
return errors.New("insufficient funds")
|
||||
}
|
||||
block.Mapping[tx.Sender] = balanceInChain - balanceInTX
|
||||
block.addBalance(chain, tx.Receiver, tx.Value)
|
||||
block.addBalance(chain, STORAGE_CHAIN, tx.ToStorage)
|
||||
block.Transactions = append(block.Transactions, *tx)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (chain *BlockChain) Balance(address string) uint64 {
|
||||
var (
|
||||
sblock string
|
||||
block *Block
|
||||
balance uint64
|
||||
)
|
||||
rows, err := chain.DB.Query("SELECT Block FROM BlockChain WHERE Id <= $1 ORDER BY Id DESC",
|
||||
chain.index)
|
||||
if err != nil {
|
||||
return balance
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
rows.Scan(&sblock)
|
||||
block = DeserializeBlock(sblock)
|
||||
if value, ok := block.Mapping[address]; ok {
|
||||
balance = value
|
||||
break
|
||||
}
|
||||
}
|
||||
return balance
|
||||
}
|
||||
|
||||
func (block *Block) addBalance(chain *BlockChain, receiver string, value uint64) {
|
||||
var balanceInChain uint64
|
||||
if v, ok := block.Mapping[receiver]; ok {
|
||||
balanceInChain = v
|
||||
} else {
|
||||
balanceInChain = chain.Balance(receiver)
|
||||
}
|
||||
block.Mapping[receiver] = balanceInChain + value
|
||||
}
|
||||
|
||||
func DeserializeBlock(data string) *Block {
|
||||
var block Block
|
||||
err := json.Unmarshal([]byte(data), &block)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &block
|
||||
}
|
||||
|
||||
func (block *Block) Accept(chain *BlockChain, user *User, ch chan bool) error {
|
||||
if !block.transactionsIsValid(chain) {
|
||||
return errors.New("transactions is not valid")
|
||||
}
|
||||
block.AddTransaction(chain, &Transaction{
|
||||
RandBytes: GenerateRandomBytes(RAND_BYTES),
|
||||
Sender: STORAGE_CHAIN,
|
||||
Receiver: user.Address(),
|
||||
Value: STORAGE_REWARD,
|
||||
})
|
||||
block.TimeStamp = time.Now().Format(time.RFC3339)
|
||||
block.CurrHash = block.hash()
|
||||
block.Signature = block.sign(user.Private())
|
||||
block.Nonce = block.proof(ch)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (block *Block) transactionsIsValid(chain *BlockChain) bool {
|
||||
lentxs := len(block.Transactions)
|
||||
plusStorage := 0
|
||||
for i := 0; i < lentxs; i++ {
|
||||
if block.Transactions[i].Sender == STORAGE_CHAIN {
|
||||
plusStorage = 1
|
||||
break
|
||||
}
|
||||
}
|
||||
if lentxs == 0 || lentxs > TXS_LIMIT+plusStorage {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < lentxs-1; i++ {
|
||||
for j := i + 1; j < lentxs; j++ {
|
||||
if bytes.Equal(block.Transactions[i].RandBytes, block.Transactions[j].RandBytes) {
|
||||
return false
|
||||
}
|
||||
if block.Transactions[i].Sender == STORAGE_CHAIN &&
|
||||
block.Transactions[j].Sender == STORAGE_CHAIN {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < lentxs; i++ {
|
||||
tx := block.Transactions[i]
|
||||
if tx.Sender == STORAGE_CHAIN {
|
||||
if tx.Receiver != block.Miner || tx.Value != STORAGE_REWARD {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !tx.hashIsValid() {
|
||||
return false
|
||||
}
|
||||
if !tx.signIsValid() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !block.balanceIsValid(chain, tx.Sender) {
|
||||
return false
|
||||
}
|
||||
if !block.balanceIsValid(chain, tx.Receiver) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (block *Block) hash() []byte {
|
||||
var tempHash []byte
|
||||
for _, tx := range block.Transactions {
|
||||
tempHash = HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
tempHash,
|
||||
tx.CurrHash,
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
}
|
||||
var list []string
|
||||
for hash := range block.Mapping {
|
||||
list = append(list, hash)
|
||||
}
|
||||
sort.Strings(list)
|
||||
for _, hash := range list {
|
||||
tempHash = HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
tempHash,
|
||||
[]byte(hash),
|
||||
ToBytes(block.Mapping[hash]),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
}
|
||||
return HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
tempHash,
|
||||
ToBytes(uint64(block.Difficulty)),
|
||||
block.PrevHash,
|
||||
[]byte(block.Miner),
|
||||
[]byte(block.TimeStamp),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
}
|
||||
|
||||
func (block *Block) sign(priv *rsa.PrivateKey) []byte {
|
||||
return Sign(priv, block.CurrHash)
|
||||
}
|
||||
|
||||
func (block *Block) proof(ch chan bool) uint64 {
|
||||
return ProofOfWork(block.CurrHash, block.Difficulty, ch)
|
||||
}
|
||||
|
||||
func (tx *Transaction) hashIsValid() bool {
|
||||
return bytes.Equal(tx.hash(), tx.CurrHash)
|
||||
}
|
||||
|
||||
func (tx *Transaction) signIsValid() bool {
|
||||
return Verify(ParsePublic(tx.Sender), tx.CurrHash, tx.Signature) == nil
|
||||
}
|
||||
|
||||
func (block *Block) balanceIsValid(chain *BlockChain, address string) bool {
|
||||
if _, ok := block.Mapping[address]; !ok {
|
||||
return false
|
||||
}
|
||||
lentxs := len(block.Transactions)
|
||||
balanceInChain := chain.Balance(address)
|
||||
balanceSubBlock := uint64(0)
|
||||
balanceAddBlock := uint64(0)
|
||||
for j := 0; j < lentxs; j++ {
|
||||
tx := block.Transactions[j]
|
||||
if tx.Sender == address {
|
||||
balanceSubBlock += tx.Value + tx.ToStorage
|
||||
}
|
||||
if tx.Receiver == address {
|
||||
balanceAddBlock += tx.Value
|
||||
}
|
||||
if STORAGE_CHAIN == address {
|
||||
balanceAddBlock += tx.ToStorage
|
||||
}
|
||||
}
|
||||
if (balanceInChain + balanceAddBlock - balanceSubBlock) != block.Mapping[address] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func ProofOfWork(blockHash []byte, difficulty uint8, ch chan bool) uint64 {
|
||||
var (
|
||||
Target = big.NewInt(1)
|
||||
intHash = big.NewInt(1)
|
||||
nonce = uint64(mrand.Intn(math.MaxUint32))
|
||||
hash []byte
|
||||
)
|
||||
Target.Lsh(Target, 256 - uint(difficulty))
|
||||
for nonce < math.MaxUint64 {
|
||||
select {
|
||||
case <-ch:
|
||||
if DEBUG {
|
||||
fmt.Println()
|
||||
}
|
||||
return nonce
|
||||
default:
|
||||
hash = HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
blockHash,
|
||||
ToBytes(nonce),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
if DEBUG {
|
||||
fmt.Printf("\rMining: %s", Base64Encode(hash))
|
||||
}
|
||||
intHash.SetBytes(hash)
|
||||
if intHash.Cmp(Target) == -1 {
|
||||
if DEBUG {
|
||||
fmt.Println()
|
||||
}
|
||||
return nonce
|
||||
}
|
||||
nonce++
|
||||
}
|
||||
}
|
||||
return nonce
|
||||
}
|
||||
|
||||
func Verify(pub *rsa.PublicKey, data, sign []byte) error {
|
||||
return rsa.VerifyPSS(pub, crypto.SHA256, data, sign, nil)
|
||||
}
|
||||
|
||||
func ParsePublic(pubData string) *rsa.PublicKey {
|
||||
pub, err := x509.ParsePKCS1PublicKey(Base64Decode(pubData))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return pub
|
||||
}
|
||||
|
||||
func init() {
|
||||
mrand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func Base64Decode(data string) []byte {
|
||||
result, err := base64.StdEncoding.DecodeString(data)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func GeneratePrivate(bits uint) *rsa.PrivateKey {
|
||||
priv, err := rsa.GenerateKey(rand.Reader, int(bits))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return priv
|
||||
}
|
||||
|
||||
func StringPrivate(priv *rsa.PrivateKey) string {
|
||||
return Base64Encode(x509.MarshalPKCS1PrivateKey(priv))
|
||||
}
|
||||
|
||||
func ParsePrivate(privData string) *rsa.PrivateKey {
|
||||
pub, err := x509.ParsePKCS1PrivateKey(Base64Decode(privData))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return pub
|
||||
}
|
||||
|
||||
func NewUser() *User {
|
||||
return &User{
|
||||
PrivateKey: GeneratePrivate(KEY_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
func LoadUser(purse string) *User {
|
||||
priv := ParsePrivate(purse)
|
||||
if priv == nil {
|
||||
return nil
|
||||
}
|
||||
return &User{
|
||||
PrivateKey: priv,
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) Purse() string {
|
||||
return StringPrivate(user.Private())
|
||||
}
|
||||
|
||||
func (chain *BlockChain) LastHash() []byte {
|
||||
var hash string
|
||||
row := chain.DB.QueryRow("SELECT Hash FROM BlockChain ORDER BY Id DESC")
|
||||
row.Scan(&hash)
|
||||
return Base64Decode(hash)
|
||||
}
|
||||
|
||||
func (block *Block) IsValid(chain *BlockChain) bool {
|
||||
switch {
|
||||
case block == nil:
|
||||
return false
|
||||
case block.Difficulty != DIFFICULTY:
|
||||
return false
|
||||
case !block.hashIsValid(chain, chain.Size()):
|
||||
return false
|
||||
case !block.signIsValid():
|
||||
return false
|
||||
case !block.proofIsValid():
|
||||
return false
|
||||
case !block.mappingIsValid():
|
||||
return false
|
||||
case !block.timeIsValid(chain, chain.Size()):
|
||||
return false
|
||||
case !block.transactionsIsValid(chain):
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func SerializeTX(tx *Transaction) string {
|
||||
jsonData, err := json.MarshalIndent(*tx, "", "\t")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
func DeserializeTX(data string) *Transaction {
|
||||
var tx Transaction
|
||||
err := json.Unmarshal([]byte(data), &tx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &tx
|
||||
}
|
||||
|
||||
func (block *Block) hashIsValid(chain *BlockChain, index uint64) bool {
|
||||
if !bytes.Equal(block.hash(), block.CurrHash) {
|
||||
return false
|
||||
}
|
||||
var id uint64
|
||||
row := chain.DB.QueryRow("SELECT Id FROM BlockChain WHERE Hash=$1",
|
||||
Base64Encode(block.PrevHash))
|
||||
row.Scan(&id)
|
||||
return id == index
|
||||
}
|
||||
|
||||
func (block *Block) signIsValid() bool {
|
||||
return Verify(ParsePublic(block.Miner), block.CurrHash, block.Signature) == nil
|
||||
}
|
||||
|
||||
func (block *Block) proofIsValid() bool {
|
||||
intHash := big.NewInt(1)
|
||||
Target := big.NewInt(1)
|
||||
hash := HashSum(bytes.Join(
|
||||
[][]byte{
|
||||
block.CurrHash,
|
||||
ToBytes(block.Nonce),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
intHash.SetBytes(hash)
|
||||
Target.Lsh(Target, 256 - uint(block.Difficulty))
|
||||
if intHash.Cmp(Target) == -1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (block *Block) mappingIsValid() bool {
|
||||
for hash := range block.Mapping {
|
||||
if hash == STORAGE_CHAIN {
|
||||
continue
|
||||
}
|
||||
flag := false
|
||||
for _, tx := range block.Transactions {
|
||||
if tx.Sender == hash || tx.Receiver == hash {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (block *Block) timeIsValid(chain *BlockChain, index uint64) bool {
|
||||
btime, err := time.Parse(time.RFC3339, block.TimeStamp)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
diff := time.Now().Sub(btime)
|
||||
if diff < 0 {
|
||||
return false
|
||||
}
|
||||
var sblock string
|
||||
row := chain.DB.QueryRow("SELECT Block FROM BlockChain WHERE Hash=$1",
|
||||
Base64Encode(block.PrevHash))
|
||||
row.Scan(&sblock)
|
||||
lblock := DeserializeBlock(sblock)
|
||||
if lblock == nil {
|
||||
return false
|
||||
}
|
||||
ltime, err := time.Parse(time.RFC3339, lblock.TimeStamp)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
result := btime.Sub(ltime)
|
||||
return result > 0
|
||||
}
|
||||
@ -1,243 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
bc "./blockchain"
|
||||
nt "./network"
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if len(os.Args) < 2 {
|
||||
panic("failed: len(os.Args) < 2")
|
||||
}
|
||||
var (
|
||||
addrStr = ""
|
||||
userNewStr = ""
|
||||
userLoadStr = ""
|
||||
)
|
||||
var (
|
||||
addrExist = false
|
||||
userNewExist = false
|
||||
userLoadExist = false
|
||||
)
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
arg := os.Args[i]
|
||||
switch {
|
||||
case strings.HasPrefix(arg, "-loadaddr:"):
|
||||
addrStr = strings.Replace(arg, "-loadaddr:", "", 1)
|
||||
addrExist = true
|
||||
case strings.HasPrefix(arg, "-newuser:"):
|
||||
userNewStr = strings.Replace(arg, "-newuser:", "", 1)
|
||||
userNewExist = true
|
||||
case strings.HasPrefix(arg, "-loaduser:"):
|
||||
userLoadStr = strings.Replace(arg, "-loaduser:", "", 1)
|
||||
userLoadExist = true
|
||||
}
|
||||
}
|
||||
if !(userNewExist || userLoadExist) || !addrExist {
|
||||
panic("failed: !(userNewExist || userLoadExist) || !addrExist")
|
||||
}
|
||||
err := json.Unmarshal([]byte(readFile(addrStr)), &Addresses)
|
||||
if err != nil {
|
||||
panic("failed: load addresses")
|
||||
}
|
||||
if len(Addresses) == 0 {
|
||||
panic("failed: len(Addresses) == 0")
|
||||
}
|
||||
if userNewExist {
|
||||
User = userNew(userNewStr)
|
||||
}
|
||||
if userLoadExist {
|
||||
User = userLoad(userLoadStr)
|
||||
}
|
||||
if User == nil {
|
||||
panic("failed: load user")
|
||||
}
|
||||
}
|
||||
|
||||
func readFile(filename string) string {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func userNew(filename string) *bc.User {
|
||||
user := bc.NewUser()
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
err := writeFile(filename, user.Purse())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func userLoad(filename string) *bc.User {
|
||||
priv := readFile(filename)
|
||||
if priv == "" {
|
||||
return nil
|
||||
}
|
||||
user := bc.LoadUser(priv)
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
var (
|
||||
Addresses []string
|
||||
User *bc.User
|
||||
)
|
||||
|
||||
func writeFile(filename string, data string) error {
|
||||
return ioutil.WriteFile(filename, []byte(data), 0644)
|
||||
}
|
||||
|
||||
func handleClient() {
|
||||
var (
|
||||
message string
|
||||
splited []string
|
||||
)
|
||||
for {
|
||||
message = inputString("> ")
|
||||
splited = strings.Split(message, " ")
|
||||
switch splited[0] {
|
||||
case "/exit":
|
||||
os.Exit(0)
|
||||
case "/user":
|
||||
if len(splited) < 2 {
|
||||
fmt.Println("failed: len(user) < 2\n")
|
||||
continue
|
||||
}
|
||||
switch splited[1] {
|
||||
case "address":
|
||||
userAddress()
|
||||
case "purse":
|
||||
userPurse()
|
||||
case "balance":
|
||||
userBalance()
|
||||
}
|
||||
case "/chain":
|
||||
if len(splited) < 2 {
|
||||
fmt.Println("failed: len(chain) < 2\n")
|
||||
continue
|
||||
}
|
||||
switch splited[1] {
|
||||
case "print":
|
||||
chainPrint()
|
||||
case "tx":
|
||||
chainTX(splited[1:])
|
||||
case "balance":
|
||||
chainBalance(splited[1:])
|
||||
}
|
||||
default:
|
||||
fmt.Println("command undefined\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inputString(begin string) string {
|
||||
fmt.Print(begin)
|
||||
msg, _ := bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
return strings.Replace(msg, "\n", "", 1)
|
||||
}
|
||||
|
||||
func userAddress() {
|
||||
fmt.Println("Address:", User.Address(), "\n")
|
||||
}
|
||||
|
||||
func userPurse() {
|
||||
fmt.Println("Purse:", User.Purse(), "\n")
|
||||
}
|
||||
|
||||
func userBalance() {
|
||||
printBalance(User.Address())
|
||||
}
|
||||
|
||||
func chainPrint() {
|
||||
for i := 0; ; i++ {
|
||||
res := nt.Send(Addresses[0], &nt.Package{
|
||||
Option: GET_BLOCK,
|
||||
Data: fmt.Sprintf("%d", i),
|
||||
})
|
||||
if res.Data == "" {
|
||||
break
|
||||
}
|
||||
fmt.Printf("[%d] => %s\n", i+1, res.Data)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func chainTX(splited []string) {
|
||||
if len(splited) != 3 {
|
||||
fmt.Println("failed: len(splited) != 3\n")
|
||||
return
|
||||
}
|
||||
num, err := strconv.Atoi(splited[2])
|
||||
if err != nil {
|
||||
fmt.Println("failed: strconv.Atoi(num)\n")
|
||||
return
|
||||
}
|
||||
for _, addr := range Addresses {
|
||||
res := nt.Send(addr, &nt.Package{
|
||||
Option: GET_LHASH,
|
||||
})
|
||||
if res == nil {
|
||||
continue
|
||||
}
|
||||
tx := bc.NewTransaction(User, bc.Base64Decode(res.Data), splited[1], uint64(num))
|
||||
res = nt.Send(addr, &nt.Package{
|
||||
Option: ADD_TRNSX,
|
||||
Data: bc.SerializeTX(tx),
|
||||
})
|
||||
if res == nil {
|
||||
continue
|
||||
}
|
||||
if res.Data == "ok" {
|
||||
fmt.Printf("ok: (%s)\n", addr)
|
||||
} else {
|
||||
fmt.Printf("fail: (%s)\n", addr)
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func chainBalance(splited []string) {
|
||||
if len(splited) != 2 {
|
||||
fmt.Println("fail: len(splited) != 2\n")
|
||||
return
|
||||
}
|
||||
printBalance(splited[1])
|
||||
}
|
||||
|
||||
func printBalance(useraddr string) {
|
||||
for _, addr := range Addresses {
|
||||
res := nt.Send(addr, &nt.Package{
|
||||
Option: GET_BLNCE,
|
||||
Data: useraddr,
|
||||
})
|
||||
fmt.Printf("Balance (%s): %s coins\n", addr, res.Data)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
const (
|
||||
ADD_BLOCK = iota + 1
|
||||
ADD_TRNSX
|
||||
GET_BLOCK
|
||||
GET_LHASH
|
||||
GET_BLNCE
|
||||
)
|
||||
|
||||
func main() {
|
||||
handleClient()
|
||||
}
|
||||
@ -1,130 +0,0 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Package struct {
|
||||
Option int
|
||||
Data string
|
||||
}
|
||||
|
||||
func Send(address string, pack *Package) *Package {
|
||||
conn, err := net.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
conn.Write([]byte(SerializePackage(pack) + ENDBYTES))
|
||||
var res = new(Package)
|
||||
ch := make(chan bool)
|
||||
go func() {
|
||||
res = readPackage(conn)
|
||||
ch <- true
|
||||
}()
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(WAITTIME * time.Second):
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func SerializePackage(pack *Package) string {
|
||||
jsonData, err := json.MarshalIndent(*pack, "", "\t")
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
const (
|
||||
ENDBYTES = "\000\005\007\001\001\007\005\000"
|
||||
)
|
||||
|
||||
func readPackage(conn net.Conn) *Package {
|
||||
var (
|
||||
data string
|
||||
size = uint64(0)
|
||||
buffer = make([]byte, BUFFSIZE)
|
||||
)
|
||||
for {
|
||||
length, err := conn.Read(buffer)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
size += uint64(length)
|
||||
if size > DMAXSIZE {
|
||||
return nil
|
||||
}
|
||||
data += string(buffer[:length])
|
||||
if strings.Contains(data, ENDBYTES) {
|
||||
data = strings.Split(data, ENDBYTES)[0]
|
||||
break
|
||||
}
|
||||
}
|
||||
return DeserializePackage(data)
|
||||
}
|
||||
|
||||
const (
|
||||
WAITTIME = 5 // seconds
|
||||
DMAXSIZE = (2 << 20) // (2^20)*2 = 2MiB
|
||||
BUFFSIZE = (4 << 10) // (2^10)*4 = 4KiB
|
||||
)
|
||||
|
||||
func DeserializePackage(data string) *Package {
|
||||
var pack Package
|
||||
err := json.Unmarshal([]byte(data), &pack)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &pack
|
||||
}
|
||||
|
||||
func Listen(address string, handle func(Conn, *Package)) Listener {
|
||||
splited := strings.Split(address, ":")
|
||||
if len(splited) != 2 {
|
||||
return nil
|
||||
}
|
||||
listener, err := net.Listen("tcp", "0.0.0.0:"+splited[1])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
go serve(listener, handle)
|
||||
return Listener(listener)
|
||||
}
|
||||
|
||||
func serve(listener net.Listener, handle func(Conn, *Package)) {
|
||||
defer listener.Close()
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
go handleConn(conn, handle)
|
||||
}
|
||||
}
|
||||
func handleConn(conn net.Conn, handle func(Conn, *Package)) {
|
||||
defer conn.Close()
|
||||
pack := readPackage(conn)
|
||||
if pack == nil {
|
||||
return
|
||||
}
|
||||
handle(Conn(conn), pack)
|
||||
}
|
||||
|
||||
type Listener net.Listener
|
||||
type Conn net.Conn
|
||||
|
||||
func Handle(option int, conn Conn, pack *Package, handle func(*Package) string) bool {
|
||||
if pack.Option != option {
|
||||
return false
|
||||
}
|
||||
conn.Write([]byte(SerializePackage(&Package{
|
||||
Option: option,
|
||||
Data: handle(pack),
|
||||
}) + ENDBYTES))
|
||||
return true
|
||||
}
|
||||
|
||||
@ -1,390 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
bc "./blockchain"
|
||||
nt "./network"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"io/ioutil"
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"io"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func readFile(filename string) string {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func userNew(filename string) *bc.User {
|
||||
user := bc.NewUser()
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
err := writeFile(filename, user.Purse())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func userLoad(filename string) *bc.User {
|
||||
priv := readFile(filename)
|
||||
if priv == "" {
|
||||
return nil
|
||||
}
|
||||
user := bc.LoadUser(priv)
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
var (
|
||||
Addresses []string
|
||||
User *bc.User
|
||||
)
|
||||
|
||||
func writeFile(filename string, data string) error {
|
||||
return ioutil.WriteFile(filename, []byte(data), 0644)
|
||||
}
|
||||
|
||||
const (
|
||||
ADD_BLOCK = iota + 1
|
||||
ADD_TRNSX
|
||||
GET_BLOCK
|
||||
GET_LHASH
|
||||
GET_BLNCE
|
||||
)
|
||||
|
||||
func init() {
|
||||
if len(os.Args) < 2 {
|
||||
panic("failed: len(os.Args) < 2")
|
||||
}
|
||||
var (
|
||||
serveStr = ""
|
||||
addrStr = ""
|
||||
userNewStr = ""
|
||||
userLoadStr = ""
|
||||
chainNewStr = ""
|
||||
chainLoadStr = ""
|
||||
)
|
||||
var (
|
||||
serveExist = false
|
||||
addrExist = false
|
||||
userNewExist = false
|
||||
userLoadExist = false
|
||||
chainNewExist = false
|
||||
chainLoadExist = false
|
||||
)
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
arg := os.Args[i]
|
||||
switch {
|
||||
case strings.HasPrefix(arg, "-serve:"):
|
||||
serveStr = strings.Replace(arg, "-serve:", "", 1)
|
||||
serveExist = true
|
||||
case strings.HasPrefix(arg, "-loadaddr:"):
|
||||
addrStr = strings.Replace(arg, "-loadaddr:", "", 1)
|
||||
addrExist = true
|
||||
case strings.HasPrefix(arg, "-newuser:"):
|
||||
userNewStr = strings.Replace(arg, "-newuser:", "", 1)
|
||||
userNewExist = true
|
||||
case strings.HasPrefix(arg, "-loaduser:"):
|
||||
userLoadStr = strings.Replace(arg, "-loaduser:", "", 1)
|
||||
userLoadExist = true
|
||||
case strings.HasPrefix(arg, "-newchain:"):
|
||||
chainNewStr = strings.Replace(arg, "-newchain:", "", 1)
|
||||
chainNewExist = true
|
||||
case strings.HasPrefix(arg, "-loadchain:"):
|
||||
chainLoadStr = strings.Replace(arg, "-loadchain:", "", 1)
|
||||
chainLoadExist = true
|
||||
}
|
||||
}
|
||||
if !(userNewExist || userLoadExist) || !(chainNewExist || chainLoadExist) ||
|
||||
!serveExist || !addrExist {
|
||||
panic("failed: !(userNewExist || userLoadExist)"+
|
||||
"|| !(chainNewExist || chainLoadExist) || !serveExist || !addrExist")
|
||||
}
|
||||
|
||||
Serve = serveStr
|
||||
var addresses []string
|
||||
err := json.Unmarshal([]byte(readFile(addrStr)), &addresses)
|
||||
if err != nil {
|
||||
panic("failed: load addresses")
|
||||
}
|
||||
var mapaddr = make(map[string]bool)
|
||||
for _, addr := range addresses {
|
||||
if addr == Serve {
|
||||
continue
|
||||
}
|
||||
if _, ok := mapaddr[addr]; ok {
|
||||
continue
|
||||
}
|
||||
mapaddr[addr] = true
|
||||
Addresses = append(Addresses, addr)
|
||||
}
|
||||
if userNewExist {
|
||||
User = userNew(userNewStr)
|
||||
}
|
||||
if userLoadExist {
|
||||
User = userLoad(userLoadStr)
|
||||
}
|
||||
if User == nil {
|
||||
panic("failed: load user")
|
||||
}
|
||||
if chainNewExist {
|
||||
Filename = chainNewStr
|
||||
Chain = chainNew(chainNewStr)
|
||||
}
|
||||
if chainLoadExist {
|
||||
Filename = chainLoadStr
|
||||
Chain = chainLoad(chainLoadStr)
|
||||
}
|
||||
if Chain == nil {
|
||||
panic("failed: load chain")
|
||||
}
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
}
|
||||
|
||||
var (
|
||||
Filename string
|
||||
Serve string
|
||||
Chain *bc.BlockChain
|
||||
Block *bc.Block
|
||||
)
|
||||
|
||||
func chainNew(filename string) *bc.BlockChain {
|
||||
err := bc.NewChain(filename, User.Address())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return bc.LoadChain(filename)
|
||||
}
|
||||
|
||||
func chainLoad(filename string) *bc.BlockChain {
|
||||
chain := bc.LoadChain(filename)
|
||||
if chain == nil {
|
||||
return nil
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
func main() {
|
||||
nt.Listen(Serve, handleServer)
|
||||
for {
|
||||
fmt.Scanln()
|
||||
}
|
||||
}
|
||||
|
||||
func handleServer(conn nt.Conn, pack *nt.Package) {
|
||||
nt.Handle(ADD_BLOCK, conn, pack, addBlock)
|
||||
nt.Handle(ADD_TRNSX, conn, pack, addTransaction)
|
||||
nt.Handle(GET_BLOCK, conn, pack, getBlock)
|
||||
nt.Handle(GET_LHASH, conn, pack, getLastHash)
|
||||
nt.Handle(GET_BLNCE, conn, pack, getBalance)
|
||||
}
|
||||
|
||||
func addBlock(pack *nt.Package) string {
|
||||
splited := strings.Split(pack.Data, SEPARATOR)
|
||||
if len(splited) != 3 {
|
||||
return "fail"
|
||||
}
|
||||
block := bc.DeserializeBlock(splited[2])
|
||||
if !block.IsValid(Chain) {
|
||||
currSize := Chain.Size()
|
||||
num, err := strconv.Atoi(splited[1])
|
||||
if err != nil {
|
||||
return "fail"
|
||||
}
|
||||
if currSize < uint64(num) {
|
||||
go compareChains(splited[0], uint64(num))
|
||||
return "ok "
|
||||
}
|
||||
return "fail"
|
||||
}
|
||||
Mutex.Lock()
|
||||
Chain.AddBlock(block)
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
Mutex.Unlock()
|
||||
if IsMining {
|
||||
BreakMining <- true
|
||||
IsMining = false
|
||||
}
|
||||
return "ok"
|
||||
}
|
||||
|
||||
func addTransaction(pack *nt.Package) string {
|
||||
var tx = bc.DeserializeTX(pack.Data)
|
||||
if tx == nil || len(Block.Transactions) == bc.TXS_LIMIT {
|
||||
return "fail"
|
||||
}
|
||||
Mutex.Lock()
|
||||
err := Block.AddTransaction(Chain, tx)
|
||||
Mutex.Unlock()
|
||||
if err != nil {
|
||||
return "fail"
|
||||
}
|
||||
if len(Block.Transactions) == bc.TXS_LIMIT {
|
||||
go func() {
|
||||
Mutex.Lock()
|
||||
block := *Block
|
||||
IsMining = true
|
||||
Mutex.Unlock()
|
||||
res := (&block).Accept(Chain, User, BreakMining)
|
||||
Mutex.Lock()
|
||||
IsMining = false
|
||||
if res == nil && bytes.Equal(block.PrevHash, Block.PrevHash) {
|
||||
Chain.AddBlock(&block)
|
||||
pushBlockToNet(&block)
|
||||
}
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
Mutex.Unlock()
|
||||
}()
|
||||
}
|
||||
return "ok"
|
||||
}
|
||||
|
||||
func getBlock(pack *nt.Package) string {
|
||||
num, err := strconv.Atoi(pack.Data)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
size := Chain.Size()
|
||||
if uint64(num) < size {
|
||||
return selectBlock(Chain, num)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getLastHash(pack *nt.Package) string {
|
||||
return bc.Base64Encode(Chain.LastHash())
|
||||
}
|
||||
|
||||
func getBalance(pack *nt.Package) string {
|
||||
return fmt.Sprintf("%d", Chain.Balance(pack.Data))
|
||||
}
|
||||
|
||||
const (
|
||||
SEPARATOR = "_SEPARATOR_"
|
||||
)
|
||||
|
||||
var (
|
||||
IsMining bool
|
||||
BreakMining = make(chan bool)
|
||||
)
|
||||
|
||||
func compareChains(address string, num uint64) string {
|
||||
filename := "temp_" + hex.EncodeToString(bc.GenerateRandomBytes(8))
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return "fail"
|
||||
}
|
||||
file.Close()
|
||||
defer func() {
|
||||
os.Remove(filename)
|
||||
}()
|
||||
res := nt.Send(address, &nt.Package{
|
||||
Option: GET_BLOCK,
|
||||
Data: fmt.Sprintf("%d", 0),
|
||||
})
|
||||
if res == nil {
|
||||
return "fail"
|
||||
}
|
||||
genesis := bc.DeserializeBlock(res.Data)
|
||||
if genesis == nil {
|
||||
return "fail"
|
||||
}
|
||||
db, err := sql.Open("sqlite3", filename)
|
||||
if err != nil {
|
||||
return "fail"
|
||||
}
|
||||
defer db.Close()
|
||||
_, err = db.Exec(bc.CREATE_TABLE)
|
||||
chain := &bc.BlockChain{
|
||||
DB: db,
|
||||
}
|
||||
chain.AddBlock(genesis)
|
||||
defer func() {
|
||||
chain.DB.Close()
|
||||
}()
|
||||
for i := uint64(1); i < num; i++ {
|
||||
res := nt.Send(address, &nt.Package{
|
||||
Option: GET_BLOCK,
|
||||
Data: fmt.Sprintf("%d", i),
|
||||
})
|
||||
if res == nil {
|
||||
return "fail"
|
||||
}
|
||||
block := bc.DeserializeBlock(res.Data)
|
||||
if block == nil {
|
||||
return "fail"
|
||||
}
|
||||
if !block.IsValid(chain) {
|
||||
return "fail"
|
||||
}
|
||||
chain.AddBlock(block)
|
||||
}
|
||||
Mutex.Lock()
|
||||
Chain.DB.Close()
|
||||
os.Remove(Filename)
|
||||
copyFile(filename, Filename)
|
||||
Chain = bc.LoadChain(Filename)
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
Mutex.Unlock()
|
||||
if IsMining {
|
||||
BreakMining <- true
|
||||
IsMining = false
|
||||
}
|
||||
return "ok"
|
||||
}
|
||||
|
||||
var (
|
||||
Mutex sync.Mutex
|
||||
)
|
||||
|
||||
func pushBlockToNet(block *bc.Block) {
|
||||
var (
|
||||
sblock = bc.SerializeBlock(block)
|
||||
msg = Serve + SEPARATOR + fmt.Sprintf("%d", Chain.Size()) + SEPARATOR + sblock
|
||||
)
|
||||
for _, addr := range Addresses {
|
||||
go nt.Send(addr, &nt.Package{
|
||||
Option: ADD_BLOCK,
|
||||
Data: msg,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func selectBlock(chain *bc.BlockChain, i int) string {
|
||||
var block string
|
||||
row := chain.DB.QueryRow("SELECT Block FROM BlockChain WHERE Id=$1", i+1)
|
||||
row.Scan(&block)
|
||||
return block
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return out.Close()
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user