delete older code

This commit is contained in:
server 2020-07-19 10:35:22 -04:00
parent 5c6458a9d4
commit 56cb813379
10 changed files with 2 additions and 2373 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
main
mainbc
mainnt
node
client
node1.key

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -1,4 +0,0 @@
[
":8080",
":9090"
]

View File

@ -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
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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()
}