diff --git a/.gitignore b/.gitignore index 15d327b..d5b7760 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ -main +mainbc +mainnt node client node1.key diff --git a/_example/_blockchain/blockchain/blockchain.go b/_example/_blockchain/blockchain/blockchain.go deleted file mode 100644 index b424731..0000000 --- a/_example/_blockchain/blockchain/blockchain.go +++ /dev/null @@ -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 -} diff --git a/_example/_blockchain/main.go b/_example/_blockchain/main.go deleted file mode 100644 index e62a1df..0000000 --- a/_example/_blockchain/main.go +++ /dev/null @@ -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) - } -} diff --git a/_example/_network/main.go b/_example/_network/main.go deleted file mode 100644 index 4edc62b..0000000 --- a/_example/_network/main.go +++ /dev/null @@ -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) -} diff --git a/_example/_network/network/network.go b/_example/_network/network/network.go deleted file mode 100644 index 34cc5b0..0000000 --- a/_example/_network/network/network.go +++ /dev/null @@ -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 -} - diff --git a/_example/_result/addr.json b/_example/_result/addr.json deleted file mode 100644 index 3cb1ce6..0000000 --- a/_example/_result/addr.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - ":8080", - ":9090" -] diff --git a/_example/_result/blockchain/blockchain.go b/_example/_result/blockchain/blockchain.go deleted file mode 100644 index b424731..0000000 --- a/_example/_result/blockchain/blockchain.go +++ /dev/null @@ -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 -} diff --git a/_example/_result/client.go b/_example/_result/client.go deleted file mode 100644 index b4d6e39..0000000 --- a/_example/_result/client.go +++ /dev/null @@ -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() -} diff --git a/_example/_result/network/network.go b/_example/_result/network/network.go deleted file mode 100644 index 34cc5b0..0000000 --- a/_example/_result/network/network.go +++ /dev/null @@ -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 -} - diff --git a/_example/_result/node.go b/_example/_result/node.go deleted file mode 100644 index 1d1d150..0000000 --- a/_example/_result/node.go +++ /dev/null @@ -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() -}