mirror of
https://github.com/number571/blockchain.git
synced 2026-09-12 19:50:00 +05:00
Add files via upload
This commit is contained in:
parent
06f9493429
commit
a2c34ec03f
4
Makefile
4
Makefile
@ -1,8 +1,8 @@
|
||||
.PHONY: default build node client
|
||||
default: build
|
||||
build: node.go client.go serve.go
|
||||
go build -o node node.go serve.go
|
||||
go build -o client client.go
|
||||
go build -o node node.go serve.go values.go
|
||||
go build -o client client.go values.go
|
||||
node: node
|
||||
./node
|
||||
client: client
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"time"
|
||||
"sort"
|
||||
"bytes"
|
||||
"math/big"
|
||||
@ -48,6 +49,7 @@ func (block *Block) hash() []byte {
|
||||
ToBytes(uint64(block.Difficulty)),
|
||||
block.PrevHash,
|
||||
[]byte(block.Miner),
|
||||
[]byte(block.TimeStamp),
|
||||
},
|
||||
[]byte{},
|
||||
))
|
||||
@ -105,3 +107,27 @@ func (block *Block) mappingIsValid() bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (chain *BlockChain) timeIsValid(block *Block, index uint64) bool {
|
||||
btime, err := time.Parse(time.RFC3339, block.TimeStamp)
|
||||
if err != nil {
|
||||
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 >= TIME_SESSION
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package blockchain
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"time"
|
||||
"bytes"
|
||||
"errors"
|
||||
"database/sql"
|
||||
@ -20,19 +21,15 @@ func NewChain(filename, receiver string) error {
|
||||
return errors.New("open database")
|
||||
}
|
||||
defer db.Close()
|
||||
_, err = db.Exec(`
|
||||
CREATE TABLE BlockChain (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
Hash VARCHAR(44) UNIQUE,
|
||||
Block TEXT
|
||||
);
|
||||
`)
|
||||
_, 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
|
||||
@ -48,10 +45,48 @@ func LoadChain(filename string) *BlockChain {
|
||||
chain := &BlockChain{
|
||||
DB: db,
|
||||
}
|
||||
chain.Index = chain.Size()
|
||||
chain.index = chain.Size()
|
||||
return chain
|
||||
}
|
||||
|
||||
func (chain *BlockChain) BlockIsValid(block *Block) bool {
|
||||
// size := chain.index
|
||||
// chain.index = size - 1
|
||||
// defer func() {
|
||||
// chain.index = size
|
||||
// }()
|
||||
switch {
|
||||
case block == nil:
|
||||
println(1)
|
||||
return false
|
||||
case block.Difficulty != DIFFICULTY:
|
||||
println(2)
|
||||
return false
|
||||
case !block.hashIsValid():
|
||||
println(3)
|
||||
return false
|
||||
case !chain.hashIsValid(block, chain.Size()):
|
||||
println(4)
|
||||
return false
|
||||
case !block.signIsValid():
|
||||
println(5)
|
||||
return false
|
||||
case !block.proofIsValid():
|
||||
println(6)
|
||||
return false
|
||||
case !block.mappingIsValid():
|
||||
println(7)
|
||||
return false
|
||||
case !chain.timeIsValid(block, chain.Size()):
|
||||
println(8)
|
||||
return false
|
||||
case !chain.transactionsIsValid(block):
|
||||
println(9)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (chain *BlockChain) Size() uint64 {
|
||||
var index uint64
|
||||
row := chain.DB.QueryRow("SELECT Id FROM BlockChain ORDER BY Id DESC")
|
||||
@ -91,9 +126,15 @@ func (chain *BlockChain) PrintChain() error {
|
||||
}
|
||||
|
||||
if !block.hashIsValid() {
|
||||
fmt.Printf("[%d][FAILED] hash is not valid\n", index)
|
||||
fmt.Printf("[%d][FAILED] block hash is not valid\n", index)
|
||||
} else {
|
||||
fmt.Printf("[%d][SUCCESS] hash is valid\n", index)
|
||||
fmt.Printf("[%d][SUCCESS] block hash is valid\n", index)
|
||||
}
|
||||
|
||||
if !chain.hashIsValid(block, index - 1) {
|
||||
fmt.Printf("[%d][FAILED] chain hash is not valid\n", index)
|
||||
} else {
|
||||
fmt.Printf("[%d][SUCCESS] chain hash is valid\n", index)
|
||||
}
|
||||
|
||||
if !block.signIsValid() {
|
||||
@ -114,14 +155,20 @@ func (chain *BlockChain) PrintChain() error {
|
||||
fmt.Printf("[%d][SUCCESS] mapping is valid\n", index)
|
||||
}
|
||||
|
||||
size = chain.Index
|
||||
chain.Index = index - 1
|
||||
if !chain.timeIsValid(block, index - 1) {
|
||||
fmt.Printf("[%d][FAILED] time is not valid\n", index)
|
||||
} else {
|
||||
fmt.Printf("[%d][SUCCESS] time is valid\n", index)
|
||||
}
|
||||
|
||||
size = chain.index
|
||||
chain.index = index - 1
|
||||
if !chain.transactionsIsValid(block) {
|
||||
fmt.Printf("[%d][FAILED] transactions is not valid\n", index)
|
||||
} else {
|
||||
fmt.Printf("[%d][SUCCESS] transactions is valid\n", index)
|
||||
}
|
||||
chain.Index = size
|
||||
chain.index = size
|
||||
|
||||
print:
|
||||
fmt.Printf("[%d] => %s\n\n", index, sblock)
|
||||
@ -135,7 +182,7 @@ func (chain *BlockChain) Balance(address string) uint64 {
|
||||
block *Block
|
||||
balance uint64
|
||||
)
|
||||
rows, err := chain.DB.Query("SELECT Block FROM BlockChain WHERE Id <= $1 ORDER BY Id DESC", chain.Index)
|
||||
rows, err := chain.DB.Query("SELECT Block FROM BlockChain WHERE Id <= $1 ORDER BY Id DESC", chain.index)
|
||||
if err != nil {
|
||||
return balance
|
||||
}
|
||||
@ -158,9 +205,9 @@ func (chain *BlockChain) LastHash() []byte {
|
||||
return Base64Decode(hash)
|
||||
}
|
||||
|
||||
func (chain *BlockChain) PushBlock(user *User, block *Block) {
|
||||
func (chain *BlockChain) AcceptBlock(user *User, block *Block) *Block {
|
||||
if !chain.transactionsIsValid(block) {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
block.AddTransaction(chain, &Transaction{
|
||||
RandBytes: GenerateRandomBytes(RAND_BYTES),
|
||||
@ -168,20 +215,28 @@ func (chain *BlockChain) PushBlock(user *User, block *Block) {
|
||||
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()
|
||||
chain.AddBlock(block)
|
||||
return block
|
||||
}
|
||||
|
||||
func (chain *BlockChain) AddBlock(block *Block) {
|
||||
chain.Index += 1
|
||||
chain.index += 1
|
||||
chain.DB.Exec("INSERT INTO BlockChain (Hash, Block) VALUES ($1, $2)",
|
||||
Base64Encode(block.CurrHash),
|
||||
SerializeBlock(block),
|
||||
)
|
||||
}
|
||||
|
||||
func (chain *BlockChain) hashIsValid(block *Block, index uint64) bool {
|
||||
var id uint64
|
||||
row := chain.DB.QueryRow("SELECT Id FROM BlockChain WHERE Hash=$1", Base64Encode(block.PrevHash))
|
||||
row.Scan(&id)
|
||||
return id == index
|
||||
}
|
||||
|
||||
func (chain *BlockChain) transactionsIsValid(block *Block) bool {
|
||||
lentxs := len(block.Transactions)
|
||||
plusStorage := 0
|
||||
@ -192,16 +247,19 @@ func (chain *BlockChain) transactionsIsValid(block *Block) bool {
|
||||
}
|
||||
}
|
||||
if lentxs == 0 || lentxs > TXS_LIMIT + plusStorage {
|
||||
// fmt.Println("Q:", 1)
|
||||
return false
|
||||
}
|
||||
for i := 0; i < lentxs-1; i++ {
|
||||
for j := i+1; j < lentxs; j++ {
|
||||
// rand bytes not be equal
|
||||
if bytes.Equal(block.Transactions[i].RandBytes, block.Transactions[j].RandBytes) {
|
||||
// fmt.Println("Q:", 2)
|
||||
return false
|
||||
}
|
||||
// storage tx only one
|
||||
if block.Transactions[i].Sender == STORAGE_CHAIN && block.Transactions[j].Sender == STORAGE_CHAIN {
|
||||
// fmt.Println("Q:", 3)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -211,20 +269,25 @@ func (chain *BlockChain) transactionsIsValid(block *Block) bool {
|
||||
// storage tx has no hash and signature
|
||||
if tx.Sender == STORAGE_CHAIN {
|
||||
if tx.Receiver != block.Miner || tx.Value != STORAGE_REWARD {
|
||||
// fmt.Println("Q:", 4)
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !tx.hashIsValid() {
|
||||
// fmt.Println("Q:", 5)
|
||||
return false
|
||||
}
|
||||
if !tx.signIsValid() {
|
||||
// fmt.Println("Q:", 6)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !chain.balanceIsValid(block, tx.Sender) {
|
||||
// fmt.Println("Q:", 7)
|
||||
return false
|
||||
}
|
||||
if !chain.balanceIsValid(block, tx.Receiver) {
|
||||
// fmt.Println("Q:", 8)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,16 @@ func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
const (
|
||||
CREATE_TABLE = `
|
||||
CREATE TABLE BlockChain (
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
Hash VARCHAR(44) UNIQUE,
|
||||
Block TEXT
|
||||
);
|
||||
`
|
||||
)
|
||||
|
||||
const (
|
||||
// DEBUG = false
|
||||
DEBUG = true
|
||||
@ -19,14 +29,15 @@ const (
|
||||
STORAGE_REWARD = 1
|
||||
GENESIS_BLOCK = "GENESIS-BLOCK"
|
||||
GENESIS_REWARD = 100
|
||||
DIFFICULTY = 15
|
||||
TXS_LIMIT = 6
|
||||
TRANSFER_MAX = 10
|
||||
DIFFICULTY = 20 // 15
|
||||
TXS_LIMIT = 2 // 6
|
||||
TIME_SESSION = 1 * time.Minute// minutes
|
||||
START_PERCENT = 10
|
||||
RAND_BYTES = 32
|
||||
)
|
||||
|
||||
type BlockChain struct {
|
||||
Index uint64
|
||||
index uint64
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
@ -39,6 +50,7 @@ type Block struct {
|
||||
Mapping map[string]uint64
|
||||
Miner string // Hashname
|
||||
Signature []byte // Miner sign
|
||||
TimeStamp string // time.Now().Format(time.RFC850)
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
@ -50,4 +62,5 @@ type Transaction struct {
|
||||
ToStorage uint64
|
||||
CurrHash []byte
|
||||
Signature []byte
|
||||
// TimeStamp string
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ func NewTransaction(user *User, lasthash []byte, to string, value uint64) *Trans
|
||||
Receiver: to,
|
||||
Value: value,
|
||||
}
|
||||
if value > TRANSFER_MAX {
|
||||
if value > START_PERCENT {
|
||||
tx.ToStorage = STORAGE_REWARD
|
||||
}
|
||||
tx.CurrHash = tx.hash()
|
||||
@ -31,7 +31,7 @@ func (block *Block) AddTransaction(chain *BlockChain, tx *Transaction) error {
|
||||
if value, ok := block.Mapping[tx.Sender]; ok {
|
||||
balanceInChain = value
|
||||
}
|
||||
if tx.Value > TRANSFER_MAX && tx.ToStorage != STORAGE_REWARD {
|
||||
if tx.Value > START_PERCENT && tx.ToStorage != STORAGE_REWARD {
|
||||
return errors.New("storage reward pass")
|
||||
}
|
||||
if balanceInBlock > balanceInChain {
|
||||
|
||||
103
client.go
103
client.go
@ -12,62 +12,59 @@ import (
|
||||
bc "./blockchain"
|
||||
)
|
||||
|
||||
var (
|
||||
Addresses []string
|
||||
User *bc.User
|
||||
)
|
||||
|
||||
const (
|
||||
OPT_BLOCKCHAIN = "[GET-BLOCKCHAIN]"
|
||||
OPT_LASTHASH = "[GET-LASTHASH]"
|
||||
OPT_BALANCE = "[GET-BALANCE]"
|
||||
OPT_TRANSACTION = "[ADD-TRANSACTION]"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("failed: len(os.Args) < 2\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
var (
|
||||
addrExist = false
|
||||
userExist = false
|
||||
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, "-newuser:"):
|
||||
arg = strings.Replace(arg, "-newuser:", "", 1)
|
||||
User = userNew(arg)
|
||||
if User == nil {
|
||||
fmt.Println("failed: generate user\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
userExist = true
|
||||
case strings.HasPrefix(arg, "-loaduser:"):
|
||||
arg = strings.Replace(arg, "-loaduser:", "", 1)
|
||||
User = userLoad(arg)
|
||||
if User == nil {
|
||||
fmt.Println("failed: load user\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
userExist = true
|
||||
case strings.HasPrefix(arg, "-loadaddr:"):
|
||||
arg = strings.Replace(arg, "-loadaddr:", "", 1)
|
||||
err := json.Unmarshal([]byte(readFile(arg)), &Addresses)
|
||||
if err != nil {
|
||||
fmt.Println("failed: load addresses\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(Addresses) == 0 {
|
||||
fmt.Println("failed: len(Addresses) == 0\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
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 !userExist || !addrExist {
|
||||
fmt.Println("failed: !userExist || !addrExist\n")
|
||||
|
||||
err := json.Unmarshal([]byte(readFile(addrStr)), &Addresses)
|
||||
if err != nil {
|
||||
fmt.Println("failed: load addresses\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(Addresses) == 0 {
|
||||
fmt.Println("failed: len(Addresses) == 0\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !(userNewExist || userLoadExist) || !addrExist {
|
||||
fmt.Println("failed: !(userNewExist || userLoadExist) || !addrExist\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if userNewExist {
|
||||
User = userNew(userNewStr)
|
||||
}
|
||||
if userLoadExist {
|
||||
User = userLoad(userLoadStr)
|
||||
}
|
||||
if User == nil {
|
||||
fmt.Println("failed: load user\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@ -113,7 +110,7 @@ func handleClient() {
|
||||
func chainPrint() {
|
||||
for i := 0; ; i++ {
|
||||
res := nt.Send(Addresses[0], &nt.Package{
|
||||
Option: OPT_BLOCKCHAIN,
|
||||
Option: GET_CHAIN,
|
||||
Data: fmt.Sprintf("%d", i),
|
||||
})
|
||||
if res.Data == "" {
|
||||
@ -130,7 +127,7 @@ func chainTX(splited []string) {
|
||||
return
|
||||
}
|
||||
res := nt.Send(Addresses[0], &nt.Package{
|
||||
Option: OPT_LASTHASH,
|
||||
Option: GET_LASTHASH,
|
||||
})
|
||||
num, err := strconv.Atoi(splited[2])
|
||||
if err != nil {
|
||||
@ -140,12 +137,20 @@ func chainTX(splited []string) {
|
||||
tx := bc.NewTransaction(User, bc.Base64Decode(res.Data), splited[1], uint64(num))
|
||||
sertx := bc.SerializeTX(tx)
|
||||
for _, addr := range Addresses {
|
||||
nt.Send(addr, &nt.Package{
|
||||
Option: OPT_TRANSACTION,
|
||||
res := nt.Send(addr, &nt.Package{
|
||||
Option: ADD_TRANSACTION,
|
||||
Data: sertx,
|
||||
})
|
||||
if res == nil {
|
||||
continue
|
||||
}
|
||||
if res.Data == "ok" {
|
||||
fmt.Printf("success: (%s)\n", addr)
|
||||
} else {
|
||||
fmt.Printf("fail: (%s)\n", addr)
|
||||
}
|
||||
}
|
||||
fmt.Println("success: transaction sent\n")
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func chainBalance(splited []string) {
|
||||
@ -154,7 +159,7 @@ func chainBalance(splited []string) {
|
||||
return
|
||||
}
|
||||
res := nt.Send(Addresses[0], &nt.Package{
|
||||
Option: OPT_BALANCE,
|
||||
Option: GET_BALANCE,
|
||||
Data: splited[1],
|
||||
})
|
||||
fmt.Println("Balance:", res.Data, "coins\n")
|
||||
@ -166,7 +171,7 @@ func userBalance() {
|
||||
return
|
||||
}
|
||||
res := nt.Send(Addresses[0], &nt.Package{
|
||||
Option: OPT_BALANCE,
|
||||
Option: GET_BALANCE,
|
||||
Data: User.Address(),
|
||||
})
|
||||
fmt.Println("Balance:", res.Data, "coins\n")
|
||||
|
||||
42
examples/exmp_bc.go
Normal file
42
examples/exmp_bc.go
Normal file
@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
bc "./blockchain"
|
||||
)
|
||||
|
||||
const (
|
||||
BLOCKCHAIN_DB = "blockchain.db"
|
||||
)
|
||||
|
||||
const (
|
||||
purse1 = "MIIBPAIBAAJBAO+M9NcRJAoZucR2PoenzfTN3bpfpJuuq4bXwbzabAh+XhmY4I5LfNN016SNhjzHqp8y6uD/pdHmp6tVcXZ5Gk0CAwEAAQJBAJdiNLhVFLltWIBXWVzRJs616pGFL7lSwQMOBlkaj3stZ7koGsIcT3PaQRMf23FKq1gUzMPyey0njP4+WJ7ZTuECIQD5k7Nn8HFYAHjnadoJFTzxHzbWKEpLcULNmtFoGGL65wIhAPW3MtXdWdy5zcWFad4Rf8SCmsip4NMWAQBaIBsvyi6rAiEA9e3AVISO+7uDZ+1bZ6Xg5mzuuTr8crjJrhaHkL6vEjMCIAllSfJXlnoUOMQWx+eP77zJ6bVGmaw4qS0kRRoFB8wlAiEA8kYB4ODHHgP/4kn74gVOqDWKGOZZk2uxNTWzgQnF4f0="
|
||||
purse2 = "MIIBPAIBAAJBAOJhasuqmGs/YSTkQOsrXmiFLvdqwRVTUJCJ3LG64pietxsAZKiGs57uGHjs4nDTmMiHkeo6OxoLBlUJvGB4RV8CAwEAAQJAD72wXFsPYB23FoackP2iAeiI3IFaza3OY06CvwT8rpnxHcinpeAfxeanEraHsWNpPmzqhsx066Wuup3idJQxkQIhAP96uD/G+taZ1owsDid+aCzNjSDNFzxLqCibygv3Ghm5AiEA4teEV4XvF5yFqDMpwqXYFuGFXvwMmKuFuyhjo3b7g9cCIQCHMvByf+Ca1MqfX2kWKWUD4LuA0kgrlnYlF1yzPE9JsQIhAJNJroOJ0lG5bstk1ROuzV0l94xSCffDPyTjt7Z21h6nAiEA5atlLYR1LaId1rz1wJFMi5O2iMFJjLC1g6USqO0s5ZA="
|
||||
)
|
||||
|
||||
func main() {
|
||||
user1 := bc.LoadUser(purse1)
|
||||
user2 := bc.LoadUser(purse2)
|
||||
|
||||
err := bc.NewChain(BLOCKCHAIN_DB, user1.Address())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
chain := bc.LoadChain(BLOCKCHAIN_DB)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
block := bc.NewBlock(user2.Address(), chain.LastHash())
|
||||
block.AddTransaction(chain, block.NewTransaction(user1, "aaa", 11))
|
||||
block.AddTransaction(chain, block.NewTransaction(user1, "bbb", 11))
|
||||
chain.PushBlock(user2, block)
|
||||
}
|
||||
|
||||
chain.PrintChain()
|
||||
}
|
||||
|
||||
func fileIsExist(filename string) bool {
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
32
examples/exmp_net.go
Normal file
32
examples/exmp_net.go
Normal file
@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"./network"
|
||||
)
|
||||
|
||||
const (
|
||||
TITLE_OPTION = "[TITLE-MESSAGE]"
|
||||
SERVER_ADDR = ":8080"
|
||||
)
|
||||
|
||||
func main() {
|
||||
network.Listen(SERVER_ADDR, handleFunc)
|
||||
for i := 0; i < 3; i++ {
|
||||
pack := network.Send(SERVER_ADDR, &network.Package{
|
||||
Option: TITLE_OPTION,
|
||||
Data: "hello, world!",
|
||||
})
|
||||
fmt.Println(network.Serialize(pack))
|
||||
}
|
||||
}
|
||||
|
||||
func handleFunc(conn net.Conn, pack *network.Package) {
|
||||
network.HandleAction(TITLE_OPTION, conn, pack, handleGet)
|
||||
}
|
||||
|
||||
func handleGet(pack *network.Package) (set string) {
|
||||
return strings.ToUpper(pack.Data)
|
||||
}
|
||||
@ -9,7 +9,11 @@ type Listener net.Listener
|
||||
type Conn net.Conn
|
||||
|
||||
func Listen(address string, handle func(Conn, *Package)) Listener {
|
||||
listener, err := net.Listen("tcp", address)
|
||||
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
|
||||
}
|
||||
|
||||
116
node.go
116
node.go
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
nt "./network"
|
||||
bc "./blockchain"
|
||||
)
|
||||
@ -15,61 +16,104 @@ func init() {
|
||||
os.Exit(1)
|
||||
}
|
||||
var (
|
||||
addrExist = false
|
||||
userExist = false
|
||||
chainExist = false
|
||||
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, "-address:"):
|
||||
Address = strings.Replace(arg, "-address:", "", 1)
|
||||
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:"):
|
||||
arg = strings.Replace(arg, "-newuser:", "", 1)
|
||||
User = userNew(arg)
|
||||
if User == nil {
|
||||
fmt.Println("failed: generate user\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
userExist = true
|
||||
userNewStr = strings.Replace(arg, "-newuser:", "", 1)
|
||||
userNewExist = true
|
||||
case strings.HasPrefix(arg, "-loaduser:"):
|
||||
arg = strings.Replace(arg, "-loaduser:", "", 1)
|
||||
User = userLoad(arg)
|
||||
if User == nil {
|
||||
fmt.Println("failed: load user\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
userExist = true
|
||||
userLoadStr = strings.Replace(arg, "-loaduser:", "", 1)
|
||||
userLoadExist = true
|
||||
case strings.HasPrefix(arg, "-newchain:"):
|
||||
arg = strings.Replace(arg, "-newchain:", "", 1)
|
||||
Chain = chainNew(arg)
|
||||
if Chain == nil {
|
||||
fmt.Println("failed: genesis block\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
chainExist = true
|
||||
chainNewStr = strings.Replace(arg, "-newchain:", "", 1)
|
||||
chainNewExist = true
|
||||
case strings.HasPrefix(arg, "-loadchain:"):
|
||||
arg = strings.Replace(arg, "-loadchain:", "", 1)
|
||||
Chain = chainLoad(arg)
|
||||
if Chain == nil {
|
||||
fmt.Println("failed: load chain\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
chainExist = true
|
||||
chainLoadStr = strings.Replace(arg, "-loadchain:", "", 1)
|
||||
chainLoadExist = true
|
||||
}
|
||||
}
|
||||
if !userExist || !chainExist || !addrExist {
|
||||
fmt.Println("failed: !userExist || !chainExist || !addrExist\n")
|
||||
|
||||
if !(userNewExist || userLoadExist) || !(chainNewExist || chainLoadExist) || !serveExist || !addrExist {
|
||||
fmt.Println("failed: !(userNewExist || userLoadExist) || !(chainNewExist || chainLoadExist) || !serveExist || !addrExist\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
Serve = serveStr
|
||||
|
||||
var addresses []string
|
||||
err := json.Unmarshal([]byte(readFile(addrStr)), &addresses)
|
||||
if err != nil {
|
||||
fmt.Println("failed: load addresses\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
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 {
|
||||
fmt.Println("failed: load user\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if chainNewExist {
|
||||
Filename = chainNewStr
|
||||
Chain = chainNew(chainNewStr)
|
||||
}
|
||||
if chainLoadExist {
|
||||
Filename = chainLoadStr
|
||||
Chain = chainLoad(chainLoadStr)
|
||||
}
|
||||
if Chain == nil {
|
||||
fmt.Println("failed: load chain\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
// работа с адресами
|
||||
}
|
||||
|
||||
func main() {
|
||||
nt.Listen(Address, handleServer)
|
||||
nt.Listen(Serve, handleServer)
|
||||
for {
|
||||
fmt.Scanln()
|
||||
Chain.PrintChain()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
260
serve.go
260
serve.go
@ -1,77 +1,231 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
bc "./blockchain"
|
||||
nt "./network"
|
||||
)
|
||||
|
||||
var (
|
||||
Address string
|
||||
Chain *bc.BlockChain
|
||||
Block *bc.Block
|
||||
User *bc.User
|
||||
)
|
||||
|
||||
const (
|
||||
OPT_BLOCKCHAIN = "[GET-BLOCKCHAIN]"
|
||||
OPT_LASTHASH = "[GET-LASTHASH]"
|
||||
OPT_BALANCE = "[GET-BALANCE]"
|
||||
OPT_TRANSACTION = "[ADD-TRANSACTION]"
|
||||
"io"
|
||||
"os"
|
||||
"fmt"
|
||||
"time"
|
||||
"bytes"
|
||||
"strings"
|
||||
"strconv"
|
||||
"encoding/hex"
|
||||
bc "./blockchain"
|
||||
nt "./network"
|
||||
)
|
||||
|
||||
func handleServer(conn nt.Conn, pack *nt.Package) {
|
||||
nt.Handle(OPT_BLOCKCHAIN, conn, pack, getBlockChain)
|
||||
nt.Handle(OPT_LASTHASH, conn, pack, getLastHash)
|
||||
nt.Handle(OPT_BALANCE, conn, pack, getBalance)
|
||||
nt.Handle(OPT_TRANSACTION, conn, pack, addTransaction)
|
||||
nt.Handle(GET_SIZE, conn, pack, getSize)
|
||||
nt.Handle(ADD_BLOCK, conn, pack, addBlock)
|
||||
nt.Handle(GET_CHAIN, conn, pack, getChain)
|
||||
nt.Handle(GET_LASTHASH, conn, pack, getLastHash)
|
||||
nt.Handle(GET_BALANCE, conn, pack, getBalance)
|
||||
nt.Handle(ADD_TRANSACTION, conn, pack, addTransaction)
|
||||
}
|
||||
|
||||
func getBlockChain(pack *nt.Package) string {
|
||||
if Chain == nil {
|
||||
return ""
|
||||
}
|
||||
num, err := strconv.Atoi(pack.Data)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if uint64(num) < Chain.Index {
|
||||
return getBlock(Chain, num)
|
||||
}
|
||||
return ""
|
||||
func getSize(pack *nt.Package) string {
|
||||
return fmt.Sprintf("%d", Chain.Size())
|
||||
}
|
||||
|
||||
func addBlock(pack *nt.Package) string {
|
||||
println(111)
|
||||
splited := strings.Split(pack.Data, SEPARATOR)
|
||||
if len(splited) != 3 {
|
||||
println(222)
|
||||
return "fail"
|
||||
}
|
||||
block := bc.DeserializeBlock(splited[2])
|
||||
if !Chain.BlockIsValid(block) {
|
||||
currSize := Chain.Size()
|
||||
num, err := strconv.Atoi(splited[1])
|
||||
if err != nil {
|
||||
println(333)
|
||||
return "fail"
|
||||
}
|
||||
if currSize < uint64(num) {
|
||||
return compareChains(splited[0], uint64(num))
|
||||
}
|
||||
println(444)
|
||||
return "fail"
|
||||
}
|
||||
|
||||
Chain.AddBlock(block)
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
|
||||
println(555)
|
||||
return "ok"
|
||||
}
|
||||
|
||||
func compareChains(address string, num uint64) string {
|
||||
filename := "temp_" + hex.EncodeToString(bc.GenerateRandomBytes(8))
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
println(111666)
|
||||
return "fail"
|
||||
}
|
||||
file.Close()
|
||||
defer func() {
|
||||
os.Remove(filename)
|
||||
}()
|
||||
|
||||
res := nt.Send(address, &nt.Package{
|
||||
Option: GET_CHAIN,
|
||||
Data: fmt.Sprintf("%d", 0),
|
||||
})
|
||||
if res == nil {
|
||||
println(777)
|
||||
return "fail"
|
||||
}
|
||||
|
||||
block := bc.DeserializeBlock(res.Data)
|
||||
if block == nil {
|
||||
println(888)
|
||||
return "fail"
|
||||
}
|
||||
|
||||
if bc.NewChain(filename, block.Miner) != nil {
|
||||
println(999)
|
||||
return "fail"
|
||||
}
|
||||
chain := bc.LoadChain(filename)
|
||||
defer func() {
|
||||
chain.DB.Close()
|
||||
}()
|
||||
|
||||
for i := uint64(1); i < num; i++ {
|
||||
res := nt.Send(address, &nt.Package{
|
||||
Option: GET_CHAIN,
|
||||
Data: fmt.Sprintf("%d", i),
|
||||
})
|
||||
if res == nil {
|
||||
println("AAA")
|
||||
return "fail"
|
||||
}
|
||||
block := bc.DeserializeBlock(res.Data)
|
||||
if block == nil {
|
||||
println("BBB")
|
||||
return "fail"
|
||||
}
|
||||
if !chain.BlockIsValid(block) {
|
||||
println("CCC")
|
||||
return "fail"
|
||||
}
|
||||
chain.AddBlock(block)
|
||||
}
|
||||
|
||||
println("DDD")
|
||||
|
||||
Chain.DB.Close()
|
||||
os.Remove(Filename)
|
||||
|
||||
copyFile(filename, Filename)
|
||||
Chain = bc.LoadChain(Filename)
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
|
||||
return "ok"
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
func getChain(pack *nt.Package) string {
|
||||
num, err := strconv.Atoi(pack.Data)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
size := Chain.Size()
|
||||
if uint64(num) < size {
|
||||
return getBlock(Chain, num)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getBlock(chain *bc.BlockChain, i int) string {
|
||||
var block string
|
||||
var block string
|
||||
row := chain.DB.QueryRow("SELECT Block FROM BlockChain WHERE Id=$1", i+1)
|
||||
row.Scan(&block)
|
||||
return block
|
||||
}
|
||||
|
||||
func getLastHash(pack *nt.Package) string {
|
||||
if Chain == nil {
|
||||
return ""
|
||||
}
|
||||
return bc.Base64Encode(Chain.LastHash())
|
||||
return bc.Base64Encode(Chain.LastHash())
|
||||
}
|
||||
|
||||
func getBalance(pack *nt.Package) string {
|
||||
if Chain == nil {
|
||||
return "-1"
|
||||
}
|
||||
return fmt.Sprintf("%d", Chain.Balance(pack.Data))
|
||||
return fmt.Sprintf("%d", Chain.Balance(pack.Data))
|
||||
}
|
||||
|
||||
func addTransaction(pack *nt.Package) string {
|
||||
var tx = bc.DeserializeTX(pack.Data)
|
||||
if tx == nil || len(Block.Transactions) == bc.TXS_LIMIT {
|
||||
return "fail"
|
||||
}
|
||||
Block.AddTransaction(Chain, tx)
|
||||
if len(Block.Transactions) == bc.TXS_LIMIT {
|
||||
Chain.PushBlock(User, Block)
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
}
|
||||
return "ok"
|
||||
var tx = bc.DeserializeTX(pack.Data)
|
||||
// println(111)
|
||||
if tx == nil || len(Block.Transactions) == bc.TXS_LIMIT {
|
||||
// println(222)
|
||||
return "fail"
|
||||
}
|
||||
if Block.AddTransaction(Chain, tx) != nil {
|
||||
// println(333)
|
||||
return "fail"
|
||||
}
|
||||
|
||||
lastBlock := getLastBlock()
|
||||
if lastBlock == nil {
|
||||
return "fail"
|
||||
}
|
||||
btime, err := time.Parse(time.RFC3339, lastBlock.TimeStamp)
|
||||
if err != nil {
|
||||
return "fail"
|
||||
}
|
||||
mod := time.Now().Sub(btime)
|
||||
diff := mod < bc.TIME_SESSION
|
||||
if len(Block.Transactions) == bc.TXS_LIMIT || (!diff && len(Block.Transactions) != 0) {
|
||||
go func() {
|
||||
if diff {
|
||||
time.Sleep(bc.TIME_SESSION - mod)
|
||||
}
|
||||
block := *Block
|
||||
res := Chain.AcceptBlock(User, &block)
|
||||
// println(666)
|
||||
if res != nil && bytes.Equal(block.PrevHash, Block.PrevHash) {
|
||||
// println(555)
|
||||
Chain.AddBlock(&block)
|
||||
pushBlockToNet(&block)
|
||||
}
|
||||
Block = bc.NewBlock(User.Address(), Chain.LastHash())
|
||||
}()
|
||||
// println(444)
|
||||
}
|
||||
return "ok"
|
||||
}
|
||||
|
||||
func getLastBlock() *bc.Block {
|
||||
var sblock string
|
||||
row := Chain.DB.QueryRow("SELECT Block FROM BlockChain ORDER BY Id DESC")
|
||||
row.Scan(&sblock)
|
||||
return bc.DeserializeBlock(sblock)
|
||||
}
|
||||
|
||||
func pushBlockToNet(block *bc.Block) {
|
||||
var sblock = bc.SerializeBlock(block)
|
||||
for _, addr := range Addresses {
|
||||
nt.Send(addr, &nt.Package{
|
||||
Option: ADD_BLOCK,
|
||||
Data: Serve + SEPARATOR + fmt.Sprintf("%d", Chain.Size()) + SEPARATOR + sblock,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
27
values.go
Normal file
27
values.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
bc "./blockchain"
|
||||
)
|
||||
|
||||
var (
|
||||
Filename string
|
||||
Addresses []string
|
||||
Serve string
|
||||
Chain *bc.BlockChain
|
||||
Block *bc.Block
|
||||
User *bc.User
|
||||
)
|
||||
|
||||
const (
|
||||
SEPARATOR = "_SEPARATOR_"
|
||||
)
|
||||
|
||||
const (
|
||||
GET_SIZE = "[GET_SIZE]"
|
||||
ADD_BLOCK = "[ADD_BLOCK]"
|
||||
GET_CHAIN = "[GET_CHAIN]"
|
||||
GET_LASTHASH = "[GET_LASTHASH]"
|
||||
GET_BALANCE = "[GET_BALANCE]"
|
||||
ADD_TRANSACTION = "[ADD_TRANSACTION]"
|
||||
)
|
||||
Loading…
Reference in New Issue
Block a user