diff --git a/Makefile b/Makefile index 14d045e..fe3d7fd 100644 --- a/Makefile +++ b/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 diff --git a/blockchain/block.go b/blockchain/block.go index 3f3eff2..4d7457a 100644 --- a/blockchain/block.go +++ b/blockchain/block.go @@ -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 +} diff --git a/blockchain/chain.go b/blockchain/chain.go index cb2449b..a0fe5fa 100644 --- a/blockchain/chain.go +++ b/blockchain/chain.go @@ -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 } } diff --git a/blockchain/settings.go b/blockchain/settings.go index b3bbcf2..ecacac1 100644 --- a/blockchain/settings.go +++ b/blockchain/settings.go @@ -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 } diff --git a/blockchain/transaction.go b/blockchain/transaction.go index 34a9113..36775ce 100644 --- a/blockchain/transaction.go +++ b/blockchain/transaction.go @@ -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 { diff --git a/client.go b/client.go index ad7d9bf..1946ed7 100644 --- a/client.go +++ b/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") diff --git a/examples/exmp_bc.go b/examples/exmp_bc.go new file mode 100644 index 0000000..4695cb9 --- /dev/null +++ b/examples/exmp_bc.go @@ -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 +} diff --git a/examples/exmp_net.go b/examples/exmp_net.go new file mode 100644 index 0000000..a0cf755 --- /dev/null +++ b/examples/exmp_net.go @@ -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) +} diff --git a/network/net.go b/network/net.go index 2c97a82..85c5391 100644 --- a/network/net.go +++ b/network/net.go @@ -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 } diff --git a/node.go b/node.go index ebf59c3..4659fce 100644 --- a/node.go +++ b/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() } } diff --git a/serve.go b/serve.go index 0c9bd74..cd567cf 100644 --- a/serve.go +++ b/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, + }) + } } diff --git a/values.go b/values.go new file mode 100644 index 0000000..1b5be8a --- /dev/null +++ b/values.go @@ -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]" +)