├── .gitignore ├── .todos ├── closed.txt ├── conf.json └── issues.json ├── Dockerfile ├── README.md ├── benchmarks └── main.go ├── cli ├── cli └── main.go ├── core ├── block.go ├── block_test.go ├── blockchain.go ├── blockchain_test.go ├── consts.go ├── crypto.go ├── crypto_test.go ├── main.go ├── message.go ├── message_test.go ├── net.go ├── pow.go ├── pow_test.go ├── transaction.go └── transaction_test.go ├── presentation.md └── scripts ├── build.sh └── docker.sh /.gitignore: -------------------------------------------------------------------------------- 1 | vms/ 2 | 3 | cli 4 | core 5 | blockchain 6 | # Created by http://www.gitignore.io 7 | 8 | ### OSX ### 9 | .DS_Store 10 | .AppleDouble 11 | .LSOverride 12 | 13 | # Icon must end with two \r 14 | Icon 15 | 16 | # Thumbnails 17 | ._* 18 | 19 | # Files that might appear on external disk 20 | .Spotlight-V100 21 | .Trashes 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | # Created by http://www.gitignore.io 31 | 32 | ### Go ### 33 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 34 | *.o 35 | *.a 36 | *.so 37 | 38 | # Folders 39 | _obj 40 | _test 41 | 42 | # Architecture specific extensions/prefixes 43 | *.[568vq] 44 | [568vq].out 45 | 46 | *.cgo1.go 47 | *.cgo2.c 48 | _cgo_defun.c 49 | _cgo_gotypes.go 50 | _cgo_export.* 51 | 52 | _testmain.go 53 | 54 | *.exe 55 | *.test 56 | *.prof 57 | 58 | -------------------------------------------------------------------------------- /.todos/closed.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | [Todos] Closed issue #2 4 | [Todos] Closed issue #3 5 | -------------------------------------------------------------------------------- /.todos/conf.json: -------------------------------------------------------------------------------- 1 | {"github_owner":"izqui","github_repo":"blockchain"} 2 | -------------------------------------------------------------------------------- /.todos/issues.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM google/golang 2 | 3 | WORKDIR /gopath/src/blockchain 4 | ADD . /gopath/src/blockchain 5 | RUN go get blockchain/cli 6 | 7 | CMD [] 8 | EXPOSE 9119 9 | ENTRYPOINT cli 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blockchain 2 | Having fun implementing a blockchain using Golang. 3 | 4 | Using [Minimum Viable Blockchain](https://artsec.hackpad.com/Blockchains-and-Bitcoins-mR2wlQ4KbVQ) 5 | 6 | ### Keys 7 | 8 | The Blockchain uses ECDSA (224 bits) keys. 9 | When a user first joins the blockchain a random key will be generated. 10 | 11 | Keys are encoded using base58. 12 | 13 | Given x, y as the components of the public key, the key is generated as following: 14 | 15 | ``` 16 | base58(BigInt(append(x as bytes, y as bytes))) 17 | ``` 18 | 19 | ### Proof of work 20 | In order to sign a transaction and send it to the network, proof of work is required. 21 | 22 | Proof of work is also required for block generation. 23 | 24 | ### Protocol 25 | 26 | The blockchain runs on port `9191` and uses TCP to handle connections among peers. 27 | 28 | ##### Message 29 | 30 | * Message type (1 byte) 31 | ``` 32 | const ( 33 | MESSAGE_GET_NODES = iota + 20 34 | MESSAGE_SEND_NODES 35 | 36 | MESSAGE_GET_TRANSACTION 37 | MESSAGE_SEND_TRANSACTION 38 | 39 | MESSAGE_GET_BLOCK 40 | MESSAGE_SEND_BLOCK 41 | ) 42 | ``` 43 | * Options (4 bytes): Data specific 44 | * Data (n bytes): Data specific 45 | 46 | ##### Transaction 47 | 48 | * Header: 49 | * From (80 bytes): Origin public key 50 | * To (80 bytes): Destination public key 51 | * Timestamp (4 bytes): int32 UNIX timestamp 52 | * Payload Hash (32 bytes): sha256(payloadData) 53 | * Payload Length (4 bytes): len(payloadData) 54 | * Nonce (4 bytes): Proof of work 55 | 56 | * Signature (80 bytes): signed(sha256(header)) 57 | * Payload data (Payload Length bytes): raw data 58 | 59 | ##### Block 60 | 61 | * Header: 62 | * Origin (80 bytes): Origin public key 63 | * Timestamp (4 bytes): int32 UNIX timestamp 64 | * Previous block (32 bytes): sha256(previous block header) 65 | * Merkel Root (32 Bytes): sha256(transaction hashes) 66 | * Nonce (4 bytes): int32 UNIX timestamp 67 | 68 | * Signature (80 bytes): signed(sha256(header)) 69 | * Block transactions 70 | -------------------------------------------------------------------------------- /benchmarks/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/izqui/blockchain/core" 8 | ) 9 | 10 | func main() { 11 | 12 | fmt.Println("Benchmarking...") 13 | 14 | t1 := benchmark(func() { 15 | t := core.NewTransaction(nil, nil, nil) 16 | t.GenerateNonce(core.TRANSACTION_POW) 17 | t.Sign(core.GenerateNewKeypair()) 18 | }) 19 | fmt.Println("Transaction took", t1) 20 | 21 | t2 := benchmark(func() { 22 | b := core.NewBlock(nil) 23 | b.GenerateMerkelRoot() 24 | b.GenerateNonce(core.BLOCK_POW) 25 | b.Sign(core.GenerateNewKeypair()) 26 | }) 27 | fmt.Println("Block took", t2) 28 | } 29 | 30 | func benchmark(f func()) time.Duration { 31 | 32 | t0 := time.Now() 33 | 34 | f() 35 | 36 | return time.Since(t0) 37 | } 38 | -------------------------------------------------------------------------------- /cli/cli: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/izqui/blockchain/4832a38ed82580572b5cb35b2603713b9926ee16/cli/cli -------------------------------------------------------------------------------- /cli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/izqui/blockchain/core" 10 | ) 11 | 12 | var address = flag.String("ip", fmt.Sprintf("%s:%s", core.GetIpAddress()[0], core.BLOCKCHAIN_PORT), "Public facing ip address") 13 | 14 | func init() { 15 | flag.Parse() 16 | } 17 | 18 | func main() { 19 | 20 | core.Start(*address) 21 | 22 | for { 23 | str := <-ReadStdin() 24 | core.Core.Blockchain.TransactionsQueue <- core.CreateTransaction(str) 25 | } 26 | } 27 | 28 | func ReadStdin() chan string { 29 | 30 | cb := make(chan string) 31 | sc := bufio.NewScanner(os.Stdin) 32 | 33 | go func() { 34 | if sc.Scan() { 35 | cb <- sc.Text() 36 | } 37 | }() 38 | 39 | return cb 40 | } 41 | -------------------------------------------------------------------------------- /core/block.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "reflect" 7 | 8 | "github.com/izqui/functional" 9 | "github.com/izqui/helpers" 10 | ) 11 | 12 | type BlockSlice []Block 13 | 14 | func (bs BlockSlice) Exists(b Block) bool { 15 | 16 | //Traverse array in reverse order because if a block exists is more likely to be on top. 17 | l := len(bs) 18 | for i := l - 1; i >= 0; i-- { 19 | 20 | bb := bs[i] 21 | if reflect.DeepEqual(b.Signature, bb.Signature) { 22 | return true 23 | } 24 | } 25 | 26 | return false 27 | } 28 | 29 | func (bs BlockSlice) PreviousBlock() *Block { 30 | l := len(bs) 31 | if l == 0 { 32 | return nil 33 | } else { 34 | return &bs[l-1] 35 | } 36 | } 37 | 38 | type Block struct { 39 | *BlockHeader 40 | Signature []byte 41 | *TransactionSlice 42 | } 43 | 44 | type BlockHeader struct { 45 | Origin []byte 46 | PrevBlock []byte 47 | MerkelRoot []byte 48 | Timestamp uint32 49 | Nonce uint32 50 | } 51 | 52 | func NewBlock(previousBlock []byte) Block { 53 | 54 | header := &BlockHeader{PrevBlock: previousBlock} 55 | return Block{header, nil, new(TransactionSlice)} 56 | } 57 | 58 | func (b *Block) AddTransaction(t *Transaction) { 59 | newSlice := b.TransactionSlice.AddTransaction(*t) 60 | b.TransactionSlice = &newSlice 61 | } 62 | 63 | func (b *Block) Sign(keypair *Keypair) []byte { 64 | 65 | s, _ := keypair.Sign(b.Hash()) 66 | return s 67 | } 68 | 69 | func (b *Block) VerifyBlock(prefix []byte) bool { 70 | 71 | headerHash := b.Hash() 72 | merkel := b.GenerateMerkelRoot() 73 | 74 | return reflect.DeepEqual(merkel, b.BlockHeader.MerkelRoot) && CheckProofOfWork(prefix, headerHash) && SignatureVerify(b.BlockHeader.Origin, b.Signature, headerHash) 75 | } 76 | 77 | func (b *Block) Hash() []byte { 78 | 79 | headerHash, _ := b.BlockHeader.MarshalBinary() 80 | return helpers.SHA256(headerHash) 81 | } 82 | 83 | func (b *Block) GenerateNonce(prefix []byte) uint32 { 84 | 85 | newB := b 86 | for { 87 | 88 | if CheckProofOfWork(prefix, newB.Hash()) { 89 | break 90 | } 91 | 92 | newB.BlockHeader.Nonce++ 93 | } 94 | 95 | return newB.BlockHeader.Nonce 96 | } 97 | func (b *Block) GenerateMerkelRoot() []byte { 98 | 99 | var merkell func(hashes [][]byte) []byte 100 | merkell = func(hashes [][]byte) []byte { 101 | 102 | l := len(hashes) 103 | if l == 0 { 104 | return nil 105 | } 106 | if l == 1 { 107 | return hashes[0] 108 | } else { 109 | 110 | if l%2 == 1 { 111 | return merkell([][]byte{merkell(hashes[:l-1]), hashes[l-1]}) 112 | } 113 | 114 | bs := make([][]byte, l/2) 115 | for i, _ := range bs { 116 | j, k := i*2, (i*2)+1 117 | bs[i] = helpers.SHA256(append(hashes[j], hashes[k]...)) 118 | } 119 | return merkell(bs) 120 | } 121 | } 122 | 123 | ts := functional.Map(func(t Transaction) []byte { return t.Hash() }, []Transaction(*b.TransactionSlice)).([][]byte) 124 | return merkell(ts) 125 | 126 | } 127 | func (b *Block) MarshalBinary() ([]byte, error) { 128 | 129 | bhb, err := b.BlockHeader.MarshalBinary() 130 | if err != nil { 131 | return nil, err 132 | } 133 | sig := helpers.FitBytesInto(b.Signature, NETWORK_KEY_SIZE) 134 | tsb, err := b.TransactionSlice.MarshalBinary() 135 | if err != nil { 136 | return nil, err 137 | } 138 | 139 | return append(append(bhb, sig...), tsb...), nil 140 | } 141 | 142 | func (b *Block) UnmarshalBinary(d []byte) error { 143 | 144 | buf := bytes.NewBuffer(d) 145 | 146 | header := new(BlockHeader) 147 | err := header.UnmarshalBinary(buf.Next(BLOCK_HEADER_SIZE)) 148 | if err != nil { 149 | return err 150 | } 151 | 152 | b.BlockHeader = header 153 | b.Signature = helpers.StripByte(buf.Next(NETWORK_KEY_SIZE), 0) 154 | 155 | ts := new(TransactionSlice) 156 | err = ts.UnmarshalBinary(buf.Next(helpers.MaxInt)) 157 | if err != nil { 158 | return err 159 | } 160 | 161 | b.TransactionSlice = ts 162 | 163 | return nil 164 | } 165 | 166 | func (h *BlockHeader) MarshalBinary() ([]byte, error) { 167 | 168 | buf := new(bytes.Buffer) 169 | 170 | buf.Write(helpers.FitBytesInto(h.Origin, NETWORK_KEY_SIZE)) 171 | binary.Write(buf, binary.LittleEndian, h.Timestamp) 172 | buf.Write(helpers.FitBytesInto(h.PrevBlock, 32)) 173 | buf.Write(helpers.FitBytesInto(h.MerkelRoot, 32)) 174 | binary.Write(buf, binary.LittleEndian, h.Nonce) 175 | 176 | return buf.Bytes(), nil 177 | } 178 | 179 | func (h *BlockHeader) UnmarshalBinary(d []byte) error { 180 | 181 | buf := bytes.NewBuffer(d) 182 | h.Origin = helpers.StripByte(buf.Next(NETWORK_KEY_SIZE), 0) 183 | binary.Read(bytes.NewBuffer(buf.Next(4)), binary.LittleEndian, &h.Timestamp) 184 | h.PrevBlock = buf.Next(32) 185 | h.MerkelRoot = buf.Next(32) 186 | binary.Read(bytes.NewBuffer(buf.Next(4)), binary.LittleEndian, &h.Nonce) 187 | 188 | return nil 189 | } 190 | -------------------------------------------------------------------------------- /core/block_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/izqui/helpers" 8 | ) 9 | 10 | func TestMerkellHash(t *testing.T) { 11 | 12 | tr1 := NewTransaction(nil, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))) 13 | tr2 := NewTransaction(nil, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))) 14 | tr3 := NewTransaction(nil, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))) 15 | tr4 := NewTransaction(nil, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))) 16 | 17 | b := new(Block) 18 | b.TransactionSlice = &TransactionSlice{*tr1, *tr2, *tr3, *tr4} 19 | 20 | mt := b.GenerateMerkelRoot() 21 | manual := helpers.SHA256(append(helpers.SHA256(append(tr1.Hash(), tr2.Hash()...)), helpers.SHA256(append(tr3.Hash(), tr4.Hash()...))...)) 22 | 23 | if !reflect.DeepEqual(mt, manual) { 24 | t.Error("Merkel tree generation fails") 25 | } 26 | } 27 | 28 | //TODO: Write block validation and marshalling tests [Issue: https://github.com/izqui/blockchain/issues/2] 29 | 30 | /* 31 | func TestBlockMarshalling(t *testing.T) { 32 | 33 | kp := GenerateNewKeypair() 34 | tr := NewTransaction(kp.Public, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))) 35 | 36 | tr.Header.Nonce = tr.GenerateNonce(helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, TEST_POW_PREFIX)) 37 | tr.Signature = tr.Sign(kp) 38 | 39 | data, err := tr.MarshalBinary() 40 | 41 | if err != nil { 42 | t.Error(err) 43 | } 44 | 45 | newT := &Transaction{} 46 | err = newT.UnmarshalBinary(data) 47 | 48 | if err != nil { 49 | t.Error(err) 50 | } 51 | 52 | if !reflect.DeepEqual(*newT, *tr) { 53 | t.Error("Marshall, unmarshall failed") 54 | } 55 | } 56 | 57 | func TestBlockVerification(t *testing.T) { 58 | 59 | pow := helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, TEST_POW_PREFIX) 60 | 61 | kp := GenerateNewKeypair() 62 | tr := NewTransaction(kp.Public, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024)))) 63 | 64 | tr.Header.Nonce = tr.GenerateNonce(pow) 65 | tr.Signature = tr.Sign(kp) 66 | 67 | if !tr.VerifyTransaction(pow) { 68 | 69 | t.Error("Validation failing") 70 | } 71 | } 72 | 73 | func TestIncorrectBlockPOWVerification(t *testing.T) { 74 | 75 | pow := helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, TEST_POW_PREFIX) 76 | powIncorrect := helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, 'a') 77 | 78 | kp := GenerateNewKeypair() 79 | tr := NewTransaction(kp.Public, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024)))) 80 | tr.Header.Nonce = tr.GenerateNonce(powIncorrect) 81 | tr.Signature = tr.Sign(kp) 82 | 83 | if tr.VerifyTransaction(pow) { 84 | 85 | t.Error("Passed validation without pow") 86 | } 87 | } 88 | 89 | func TestIncorrectBlockSignatureVerification(t *testing.T) { 90 | 91 | pow := helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, TEST_POW_PREFIX) 92 | kp1, kp2 := GenerateNewKeypair(), GenerateNewKeypair() 93 | tr := NewTransaction(kp2.Public, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024)))) 94 | tr.Header.Nonce = tr.GenerateNonce(pow) 95 | tr.Signature = tr.Sign(kp1) 96 | 97 | if tr.VerifyTransaction(pow) { 98 | 99 | t.Error("Passed validation with incorrect key") 100 | } 101 | } 102 | */ 103 | -------------------------------------------------------------------------------- /core/blockchain.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "time" 7 | 8 | "github.com/izqui/helpers" 9 | ) 10 | 11 | type TransactionsQueue chan *Transaction 12 | type BlocksQueue chan Block 13 | 14 | type Blockchain struct { 15 | CurrentBlock Block 16 | BlockSlice 17 | 18 | TransactionsQueue 19 | BlocksQueue 20 | } 21 | 22 | func SetupBlockchan() *Blockchain { 23 | 24 | bl := new(Blockchain) 25 | bl.TransactionsQueue, bl.BlocksQueue = make(TransactionsQueue), make(BlocksQueue) 26 | 27 | //Read blockchain from file and stuff... 28 | 29 | bl.CurrentBlock = bl.CreateNewBlock() 30 | 31 | return bl 32 | } 33 | 34 | func (bl *Blockchain) CreateNewBlock() Block { 35 | 36 | prevBlock := bl.BlockSlice.PreviousBlock() 37 | prevBlockHash := []byte{} 38 | if prevBlock != nil { 39 | 40 | prevBlockHash = prevBlock.Hash() 41 | } 42 | 43 | b := NewBlock(prevBlockHash) 44 | b.BlockHeader.Origin = Core.Keypair.Public 45 | 46 | return b 47 | } 48 | 49 | func (bl *Blockchain) AddBlock(b Block) { 50 | 51 | bl.BlockSlice = append(bl.BlockSlice, b) 52 | } 53 | 54 | func (bl *Blockchain) Run() { 55 | 56 | interruptBlockGen := bl.GenerateBlocks() 57 | for { 58 | select { 59 | case tr := <-bl.TransactionsQueue: 60 | 61 | if bl.CurrentBlock.TransactionSlice.Exists(*tr) { 62 | continue 63 | } 64 | if !tr.VerifyTransaction(TRANSACTION_POW) { 65 | fmt.Println("Recieved non valid transaction", tr) 66 | continue 67 | } 68 | 69 | bl.CurrentBlock.AddTransaction(tr) 70 | interruptBlockGen <- bl.CurrentBlock 71 | 72 | //Broadcast transaction to the network 73 | mes := NewMessage(MESSAGE_SEND_TRANSACTION) 74 | mes.Data, _ = tr.MarshalBinary() 75 | 76 | time.Sleep(300 * time.Millisecond) 77 | Core.Network.BroadcastQueue <- *mes 78 | 79 | case b := <-bl.BlocksQueue: 80 | 81 | if bl.BlockSlice.Exists(b) { 82 | fmt.Println("block exists") 83 | continue 84 | } 85 | 86 | if !b.VerifyBlock(BLOCK_POW) { 87 | fmt.Println("block verification fails") 88 | continue 89 | } 90 | 91 | if reflect.DeepEqual(b.PrevBlock, bl.CurrentBlock.Hash()) { 92 | // I'm missing some blocks in the middle. Request'em. 93 | fmt.Println("Missing blocks in between") 94 | } else { 95 | 96 | fmt.Println("New block!", b.Hash()) 97 | 98 | transDiff := TransactionSlice{} 99 | 100 | if !reflect.DeepEqual(b.BlockHeader.MerkelRoot, bl.CurrentBlock.MerkelRoot) { 101 | // Transactions are different 102 | fmt.Println("Transactions are different. finding diff") 103 | transDiff = DiffTransactionSlices(*bl.CurrentBlock.TransactionSlice, *b.TransactionSlice) 104 | } 105 | 106 | bl.AddBlock(b) 107 | 108 | //Broadcast block and shit 109 | mes := NewMessage(MESSAGE_SEND_BLOCK) 110 | mes.Data, _ = b.MarshalBinary() 111 | Core.Network.BroadcastQueue <- *mes 112 | 113 | //New Block 114 | bl.CurrentBlock = bl.CreateNewBlock() 115 | bl.CurrentBlock.TransactionSlice = &transDiff 116 | 117 | interruptBlockGen <- bl.CurrentBlock 118 | } 119 | } 120 | } 121 | } 122 | 123 | func DiffTransactionSlices(a, b TransactionSlice) (diff TransactionSlice) { 124 | //Assumes transaction arrays are sorted (which maybe is too big of an assumption) 125 | lastj := 0 126 | for _, t := range a { 127 | found := false 128 | for j := lastj; j < len(b); j++ { 129 | if reflect.DeepEqual(b[j].Signature, t.Signature) { 130 | found = true 131 | lastj = j 132 | break 133 | } 134 | } 135 | if !found { 136 | diff = append(diff, t) 137 | } 138 | } 139 | 140 | return 141 | } 142 | 143 | func (bl *Blockchain) GenerateBlocks() chan Block { 144 | 145 | interrupt := make(chan Block) 146 | 147 | go func() { 148 | 149 | block := <-interrupt 150 | loop: 151 | fmt.Println("Starting Proof of Work...") 152 | block.BlockHeader.MerkelRoot = block.GenerateMerkelRoot() 153 | block.BlockHeader.Nonce = 0 154 | block.BlockHeader.Timestamp = uint32(time.Now().Unix()) 155 | 156 | for true { 157 | 158 | sleepTime := time.Nanosecond 159 | if block.TransactionSlice.Len() > 0 { 160 | 161 | if CheckProofOfWork(BLOCK_POW, block.Hash()) { 162 | 163 | block.Signature = block.Sign(Core.Keypair) 164 | bl.BlocksQueue <- block 165 | sleepTime = time.Hour * 24 166 | fmt.Println("Found Block!") 167 | 168 | } else { 169 | 170 | block.BlockHeader.Nonce += 1 171 | } 172 | 173 | } else { 174 | sleepTime = time.Hour * 24 175 | fmt.Println("No trans sleep") 176 | } 177 | 178 | select { 179 | case block = <-interrupt: 180 | goto loop 181 | case <-helpers.Timeout(sleepTime): 182 | continue 183 | } 184 | } 185 | }() 186 | 187 | return interrupt 188 | } 189 | -------------------------------------------------------------------------------- /core/blockchain_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/izqui/helpers" 8 | ) 9 | 10 | func TestBlockDiff(t *testing.T) { 11 | 12 | tr1 := Transaction{Signature: []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))} 13 | tr2 := Transaction{Signature: []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))} 14 | tr3 := Transaction{Signature: []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))} 15 | 16 | diff := DiffTransactionSlices(TransactionSlice{tr1, tr2, tr3}, TransactionSlice{tr1, tr3}) 17 | 18 | if len(diff) != 1 && !reflect.DeepEqual(diff[0].Signature, tr2.Signature) { 19 | 20 | t.Error("Diffing algorithm fails") 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/consts.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import _ "fmt" 4 | 5 | const ( 6 | BLOCKCHAIN_PORT = "9119" 7 | MAX_NODE_CONNECTIONS = 400 8 | 9 | NETWORK_KEY_SIZE = 80 10 | 11 | TRANSACTION_HEADER_SIZE = NETWORK_KEY_SIZE /* from key */ + NETWORK_KEY_SIZE /* to key */ + 4 /* int32 timestamp */ + 32 /* sha256 payload hash */ + 4 /* int32 payload length */ + 4 /* int32 nonce */ 12 | BLOCK_HEADER_SIZE = NETWORK_KEY_SIZE /* origin key */ + 4 /* int32 timestamp */ + 32 /* prev block hash */ + 32 /* merkel tree hash */ + 4 /* int32 nonce */ 13 | 14 | KEY_POW_COMPLEXITY = 0 15 | TEST_KEY_POW_COMPLEXITY = 0 16 | 17 | TRANSACTION_POW_COMPLEXITY = 1 18 | TEST_TRANSACTION_POW_COMPLEXITY = 1 19 | 20 | BLOCK_POW_COMPLEXITY = 2 21 | TEST_BLOCK_POW_COMPLEXITY = 2 22 | 23 | KEY_SIZE = 28 24 | 25 | POW_PREFIX = 0 26 | TEST_POW_PREFIX = 0 27 | 28 | MESSAGE_TYPE_SIZE = 1 29 | MESSAGE_OPTIONS_SIZE = 4 30 | ) 31 | 32 | const ( 33 | MESSAGE_GET_NODES = iota + 20 34 | MESSAGE_SEND_NODES 35 | 36 | MESSAGE_GET_TRANSACTION 37 | MESSAGE_SEND_TRANSACTION 38 | 39 | MESSAGE_GET_BLOCK 40 | MESSAGE_SEND_BLOCK 41 | ) 42 | 43 | func SEED_NODES() []string { 44 | nodes := []string{"10.0.5.33"} 45 | 46 | /*for i := 0; i < 100; i++ { 47 | nodes = append(nodes, fmt.Sprintf("172.17.0.%d", i)) 48 | }*/ 49 | 50 | return nodes 51 | } 52 | -------------------------------------------------------------------------------- /core/crypto.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "math/big" 8 | 9 | "github.com/izqui/helpers" 10 | "github.com/tv42/base58" 11 | ) 12 | 13 | // Key generation with proof of work 14 | type Keypair struct { 15 | Public []byte `json:"public"` // base58 (x y) 16 | Private []byte `json:"private"` // d (base58 encoded) 17 | } 18 | 19 | func GenerateNewKeypair() *Keypair { 20 | 21 | pk, _ := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) 22 | 23 | b := bigJoin(KEY_SIZE, pk.PublicKey.X, pk.PublicKey.Y) 24 | 25 | public := base58.EncodeBig([]byte{}, b) 26 | private := base58.EncodeBig([]byte{}, pk.D) 27 | 28 | kp := Keypair{Public: public, Private: private} 29 | 30 | return &kp 31 | } 32 | 33 | func (k *Keypair) Sign(hash []byte) ([]byte, error) { 34 | 35 | d, err := base58.DecodeToBig(k.Private) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | b, _ := base58.DecodeToBig(k.Public) 41 | 42 | pub := splitBig(b, 2) 43 | x, y := pub[0], pub[1] 44 | 45 | key := ecdsa.PrivateKey{ecdsa.PublicKey{elliptic.P224(), x, y}, d} 46 | 47 | r, s, _ := ecdsa.Sign(rand.Reader, &key, hash) 48 | 49 | return base58.EncodeBig([]byte{}, bigJoin(KEY_SIZE, r, s)), nil 50 | } 51 | 52 | func SignatureVerify(publicKey, sig, hash []byte) bool { 53 | 54 | b, _ := base58.DecodeToBig(publicKey) 55 | publ := splitBig(b, 2) 56 | x, y := publ[0], publ[1] 57 | 58 | b, _ = base58.DecodeToBig(sig) 59 | sigg := splitBig(b, 2) 60 | r, s := sigg[0], sigg[1] 61 | 62 | pub := ecdsa.PublicKey{elliptic.P224(), x, y} 63 | 64 | return ecdsa.Verify(&pub, hash, r, s) 65 | } 66 | 67 | func bigJoin(expectedLen int, bigs ...*big.Int) *big.Int { 68 | 69 | bs := []byte{} 70 | for i, b := range bigs { 71 | 72 | by := b.Bytes() 73 | dif := expectedLen - len(by) 74 | if dif > 0 && i != 0 { 75 | 76 | by = append(helpers.ArrayOfBytes(dif, 0), by...) 77 | } 78 | 79 | bs = append(bs, by...) 80 | } 81 | 82 | b := new(big.Int).SetBytes(bs) 83 | 84 | return b 85 | } 86 | 87 | func splitBig(b *big.Int, parts int) []*big.Int { 88 | 89 | bs := b.Bytes() 90 | if len(bs)%2 != 0 { 91 | bs = append([]byte{0}, bs...) 92 | } 93 | 94 | l := len(bs) / parts 95 | as := make([]*big.Int, parts) 96 | 97 | for i, _ := range as { 98 | 99 | as[i] = new(big.Int).SetBytes(bs[i*l : (i+1)*l]) 100 | } 101 | 102 | return as 103 | 104 | } 105 | -------------------------------------------------------------------------------- /core/crypto_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/izqui/helpers" 5 | 6 | "testing" 7 | ) 8 | 9 | func TestKeyGeneration(t *testing.T) { 10 | 11 | keypair := GenerateNewKeypair() 12 | 13 | if len(keypair.Public) > 80 { 14 | t.Error("Error generating key") 15 | } 16 | } 17 | 18 | func TestKeySigning(t *testing.T) { 19 | 20 | for i := 0; i < 5; i++ { 21 | keypair := GenerateNewKeypair() 22 | 23 | data := helpers.ArrayOfBytes(i, 'a') 24 | hash := helpers.SHA256(data) 25 | 26 | signature, err := keypair.Sign(hash) 27 | 28 | if err != nil { 29 | 30 | t.Error("base58 error") 31 | 32 | } else if !SignatureVerify(keypair.Public, signature, hash) { 33 | 34 | t.Error("Signing and verifying error", len(keypair.Public)) 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/main.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | var Core = struct { 9 | *Keypair 10 | *Blockchain 11 | *Network 12 | }{} 13 | 14 | func Start(address string) { 15 | 16 | // Setup keys 17 | keypair, _ := OpenConfiguration(HOME_DIRECTORY_CONFIG) 18 | if keypair == nil { 19 | 20 | fmt.Println("Generating keypair...") 21 | keypair = GenerateNewKeypair() 22 | WriteConfiguration(HOME_DIRECTORY_CONFIG, keypair) 23 | } 24 | Core.Keypair = keypair 25 | 26 | // Setup Network 27 | Core.Network = SetupNetwork(address, BLOCKCHAIN_PORT) 28 | go Core.Network.Run() 29 | for _, n := range SEED_NODES() { 30 | Core.Network.ConnectionsQueue <- n 31 | } 32 | 33 | // Setup blockchain 34 | Core.Blockchain = SetupBlockchan() 35 | go Core.Blockchain.Run() 36 | 37 | go func() { 38 | for { 39 | select { 40 | case msg := <-Core.Network.IncomingMessages: 41 | HandleIncomingMessage(msg) 42 | } 43 | } 44 | }() 45 | } 46 | 47 | func CreateTransaction(txt string) *Transaction { 48 | 49 | t := NewTransaction(Core.Keypair.Public, nil, []byte(txt)) 50 | t.Header.Nonce = t.GenerateNonce(TRANSACTION_POW) 51 | t.Signature = t.Sign(Core.Keypair) 52 | 53 | return t 54 | } 55 | 56 | func HandleIncomingMessage(msg Message) { 57 | 58 | switch msg.Identifier { 59 | case MESSAGE_SEND_TRANSACTION: 60 | t := new(Transaction) 61 | _, err := t.UnmarshalBinary(msg.Data) 62 | if err != nil { 63 | networkError(err) 64 | break 65 | } 66 | Core.Blockchain.TransactionsQueue <- t 67 | 68 | case MESSAGE_SEND_BLOCK: 69 | b := new(Block) 70 | err := b.UnmarshalBinary(msg.Data) 71 | if err != nil { 72 | networkError(err) 73 | break 74 | } 75 | Core.Blockchain.BlocksQueue <- *b 76 | } 77 | } 78 | 79 | func logOnError(err error) { 80 | 81 | if err != nil { 82 | log.Println("[Todos] Err:", err) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /core/message.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "github.com/izqui/helpers" 7 | ) 8 | 9 | type Message struct { 10 | Identifier byte 11 | Options []byte 12 | Data []byte 13 | 14 | Reply chan Message 15 | } 16 | 17 | func NewMessage(id byte) *Message { 18 | 19 | return &Message{Identifier: id} 20 | } 21 | 22 | func (m *Message) MarshalBinary() ([]byte, error) { 23 | 24 | buf := new(bytes.Buffer) 25 | 26 | buf.WriteByte(m.Identifier) 27 | buf.Write(helpers.FitBytesInto(m.Options, MESSAGE_OPTIONS_SIZE)) 28 | buf.Write(m.Data) 29 | 30 | return buf.Bytes(), nil 31 | 32 | } 33 | 34 | func (m *Message) UnmarshalBinary(d []byte) error { 35 | 36 | buf := bytes.NewBuffer(d) 37 | 38 | if len(d) < MESSAGE_OPTIONS_SIZE+MESSAGE_TYPE_SIZE { 39 | return errors.New("Insuficient message size") 40 | } 41 | m.Identifier = buf.Next(1)[0] 42 | m.Options = helpers.StripByte(buf.Next(MESSAGE_OPTIONS_SIZE), 0) 43 | m.Data = buf.Next(helpers.MaxInt) 44 | 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /core/message_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/izqui/helpers" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestMessageMarshalling(t *testing.T) { 10 | 11 | mes := &Message{Identifier: MESSAGE_GET_NODES, Options: []byte{1, 2, 3, 4}, Data: []byte(helpers.RandomString(helpers.RandomInt(1024, 1024*4)))} 12 | bs, err := mes.MarshalBinary() 13 | 14 | if err != nil { 15 | t.Error(err) 16 | } 17 | 18 | newMes := new(Message) 19 | err = newMes.UnmarshalBinary(bs) 20 | 21 | if err != nil { 22 | t.Error(err) 23 | } 24 | 25 | if !reflect.DeepEqual(newMes, mes) { 26 | 27 | t.Error("Marshall unmarshall message error") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/net.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net" 8 | "os" 9 | "time" 10 | 11 | "github.com/izqui/helpers" 12 | ) 13 | 14 | type ConnectionsQueue chan string 15 | type NodeChannel chan *Node 16 | type Node struct { 17 | *net.TCPConn 18 | lastSeen int 19 | } 20 | 21 | type Nodes map[string]*Node 22 | 23 | type Network struct { 24 | Nodes 25 | ConnectionsQueue 26 | Address string 27 | ConnectionCallback NodeChannel 28 | BroadcastQueue chan Message 29 | IncomingMessages chan Message 30 | } 31 | 32 | func (n Nodes) AddNode(node *Node) bool { 33 | 34 | key := node.TCPConn.RemoteAddr().String() 35 | 36 | if key != Core.Network.Address && n[key] == nil { 37 | 38 | fmt.Println("Node connected", key) 39 | n[key] = node 40 | 41 | go HandleNode(node) 42 | 43 | return true 44 | } 45 | return false 46 | } 47 | 48 | func HandleNode(node *Node) { 49 | 50 | for { 51 | var bs []byte = make([]byte, 1024*1000) 52 | n, err := node.TCPConn.Read(bs[0:]) 53 | networkError(err) 54 | 55 | if err == io.EOF { 56 | fmt.Println("EOF") 57 | //TODO: Remove node [Issue: https://github.com/izqui/blockchain/issues/3] 58 | node.TCPConn.Close() 59 | break 60 | } 61 | 62 | m := new(Message) 63 | err = m.UnmarshalBinary(bs[0:n]) 64 | 65 | if err != nil { 66 | fmt.Println(err) 67 | continue 68 | } 69 | 70 | m.Reply = make(chan Message) 71 | 72 | go func(cb chan Message) { 73 | for { 74 | m, ok := <-cb 75 | 76 | if !ok { 77 | close(cb) 78 | break 79 | } 80 | 81 | b, _ := m.MarshalBinary() 82 | l := len(b) 83 | 84 | i := 0 85 | for i < l { 86 | 87 | a, _ := node.TCPConn.Write(b[i:]) 88 | i += a 89 | } 90 | } 91 | 92 | }(m.Reply) 93 | 94 | Core.Network.IncomingMessages <- *m 95 | } 96 | } 97 | 98 | func SetupNetwork(address, port string) *Network { 99 | 100 | n := new(Network) 101 | 102 | n.BroadcastQueue, n.IncomingMessages = make(chan Message), make(chan Message) 103 | n.ConnectionsQueue, n.ConnectionCallback = CreateConnectionsQueue() 104 | n.Nodes = Nodes{} 105 | n.Address = address //fmt.Sprintf("%s:%s", address, port) 106 | 107 | return n 108 | } 109 | 110 | func (n *Network) Run() { 111 | 112 | fmt.Println("Listening in", Core.Address) 113 | listenCb := StartListening(Core.Address) 114 | 115 | for { 116 | select { 117 | case node := <-listenCb: 118 | Core.Nodes.AddNode(node) 119 | 120 | case node := <-n.ConnectionCallback: 121 | Core.Nodes.AddNode(node) 122 | 123 | case message := <-n.BroadcastQueue: 124 | go n.BroadcastMessage(message) 125 | } 126 | } 127 | } 128 | 129 | func CreateConnectionsQueue() (ConnectionsQueue, NodeChannel) { 130 | 131 | in := make(ConnectionsQueue) 132 | out := make(NodeChannel) 133 | 134 | go func() { 135 | 136 | for { 137 | address := <-in 138 | 139 | address = fmt.Sprintf("%s:%s", address, BLOCKCHAIN_PORT) 140 | 141 | if address != Core.Network.Address && Core.Nodes[address] == nil { 142 | 143 | go ConnectToNode(address, 5*time.Second, false, out) 144 | } 145 | } 146 | }() 147 | 148 | return in, out 149 | } 150 | 151 | func StartListening(address string) NodeChannel { 152 | 153 | cb := make(NodeChannel) 154 | addr, err := net.ResolveTCPAddr("tcp4", address) 155 | networkError(err) 156 | 157 | listener, err := net.ListenTCP("tcp4", addr) 158 | networkError(err) 159 | 160 | go func(l *net.TCPListener) { 161 | 162 | for { 163 | connection, err := l.AcceptTCP() 164 | networkError(err) 165 | 166 | cb <- &Node{connection, int(time.Now().Unix())} 167 | } 168 | 169 | }(listener) 170 | 171 | return cb 172 | } 173 | 174 | func ConnectToNode(dst string, timeout time.Duration, retry bool, cb NodeChannel) { 175 | 176 | addrDst, err := net.ResolveTCPAddr("tcp4", dst) 177 | networkError(err) 178 | 179 | var con *net.TCPConn = nil 180 | loop: 181 | for { 182 | 183 | breakChannel := make(chan bool) 184 | go func() { 185 | 186 | con, err = net.DialTCP("tcp", nil, addrDst) 187 | 188 | if con != nil { 189 | 190 | cb <- &Node{con, int(time.Now().Unix())} 191 | breakChannel <- true 192 | } 193 | }() 194 | 195 | select { 196 | case <-helpers.Timeout(timeout): 197 | if !retry { 198 | break loop 199 | } 200 | case <-breakChannel: 201 | break loop 202 | } 203 | 204 | } 205 | } 206 | 207 | func (n *Network) BroadcastMessage(message Message) { 208 | 209 | b, _ := message.MarshalBinary() 210 | 211 | for k, node := range n.Nodes { 212 | fmt.Println("Broadcasting...", k) 213 | go func() { 214 | _, err := node.TCPConn.Write(b) 215 | if err != nil { 216 | fmt.Println("Error bcing to", node.TCPConn.RemoteAddr()) 217 | } 218 | }() 219 | } 220 | } 221 | 222 | func GetIpAddress() []string { 223 | 224 | name, err := os.Hostname() 225 | if err != nil { 226 | 227 | return nil 228 | } 229 | 230 | addrs, err := net.LookupHost(name) 231 | if err != nil { 232 | 233 | return nil 234 | } 235 | 236 | return addrs 237 | } 238 | 239 | func networkError(err error) { 240 | 241 | if err != nil && err != io.EOF { 242 | 243 | log.Println("Blockchain network: ", err) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /core/pow.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/izqui/helpers" 7 | ) 8 | 9 | var ( 10 | TRANSACTION_POW = helpers.ArrayOfBytes(TRANSACTION_POW_COMPLEXITY, POW_PREFIX) 11 | BLOCK_POW = helpers.ArrayOfBytes(BLOCK_POW_COMPLEXITY, POW_PREFIX) 12 | 13 | TEST_TRANSACTION_POW = helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, POW_PREFIX) 14 | TEST_BLOCK_POW = helpers.ArrayOfBytes(TEST_BLOCK_POW_COMPLEXITY, POW_PREFIX) 15 | ) 16 | 17 | func CheckProofOfWork(prefix []byte, hash []byte) bool { 18 | 19 | if len(prefix) > 0 { 20 | return reflect.DeepEqual(prefix, hash[:len(prefix)]) 21 | } 22 | return true 23 | } 24 | -------------------------------------------------------------------------------- /core/pow_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPow(t *testing.T) { 8 | 9 | b1 := CheckProofOfWork([]byte{0, 0, 0, 1, 2, 3}, []byte{0, 0, 0, 1, 2, 3, 4, 5}) 10 | b2 := CheckProofOfWork([]byte{0, 0}, []byte("hola")) 11 | b3 := CheckProofOfWork(BLOCK_POW, append(BLOCK_POW, 1)) 12 | b4 := CheckProofOfWork(nil, []byte("hola que tal")) 13 | 14 | if !b1 || b2 || !b3 || !b4 { 15 | t.Error("Proof of work test fails.") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/transaction.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "reflect" 8 | "time" 9 | 10 | "github.com/izqui/helpers" 11 | ) 12 | 13 | type Transaction struct { 14 | Header TransactionHeader 15 | Signature []byte 16 | Payload []byte 17 | } 18 | 19 | type TransactionHeader struct { 20 | From []byte 21 | To []byte 22 | Timestamp uint32 23 | PayloadHash []byte 24 | PayloadLength uint32 25 | Nonce uint32 26 | } 27 | 28 | // Returns bytes to be sent to the network 29 | func NewTransaction(from, to, payload []byte) *Transaction { 30 | 31 | t := Transaction{Header: TransactionHeader{From: from, To: to}, Payload: payload} 32 | 33 | t.Header.Timestamp = uint32(time.Now().Unix()) 34 | t.Header.PayloadHash = helpers.SHA256(t.Payload) 35 | t.Header.PayloadLength = uint32(len(t.Payload)) 36 | 37 | return &t 38 | } 39 | 40 | func (t *Transaction) Hash() []byte { 41 | 42 | headerBytes, _ := t.Header.MarshalBinary() 43 | return helpers.SHA256(headerBytes) 44 | } 45 | 46 | func (t *Transaction) Sign(keypair *Keypair) []byte { 47 | 48 | s, _ := keypair.Sign(t.Hash()) 49 | 50 | return s 51 | } 52 | 53 | func (t *Transaction) VerifyTransaction(pow []byte) bool { 54 | 55 | headerHash := t.Hash() 56 | payloadHash := helpers.SHA256(t.Payload) 57 | 58 | return reflect.DeepEqual(payloadHash, t.Header.PayloadHash) && CheckProofOfWork(pow, headerHash) && SignatureVerify(t.Header.From, t.Signature, headerHash) 59 | } 60 | 61 | func (t *Transaction) GenerateNonce(prefix []byte) uint32 { 62 | 63 | newT := t 64 | for { 65 | 66 | if CheckProofOfWork(prefix, newT.Hash()) { 67 | break 68 | } 69 | 70 | newT.Header.Nonce++ 71 | } 72 | 73 | return newT.Header.Nonce 74 | } 75 | 76 | func (t *Transaction) MarshalBinary() ([]byte, error) { 77 | 78 | headerBytes, _ := t.Header.MarshalBinary() 79 | 80 | if len(headerBytes) != TRANSACTION_HEADER_SIZE { 81 | return nil, errors.New("Header marshalling error") 82 | } 83 | 84 | return append(append(headerBytes, helpers.FitBytesInto(t.Signature, NETWORK_KEY_SIZE)...), t.Payload...), nil 85 | } 86 | 87 | func (t *Transaction) UnmarshalBinary(d []byte) ([]byte, error) { 88 | 89 | buf := bytes.NewBuffer(d) 90 | 91 | if len(d) < TRANSACTION_HEADER_SIZE+NETWORK_KEY_SIZE { 92 | return nil, errors.New("Insuficient bytes for unmarshalling transaction") 93 | } 94 | 95 | header := &TransactionHeader{} 96 | if err := header.UnmarshalBinary(buf.Next(TRANSACTION_HEADER_SIZE)); err != nil { 97 | return nil, err 98 | } 99 | 100 | t.Header = *header 101 | 102 | t.Signature = helpers.StripByte(buf.Next(NETWORK_KEY_SIZE), 0) 103 | t.Payload = buf.Next(int(t.Header.PayloadLength)) 104 | 105 | return buf.Next(helpers.MaxInt), nil 106 | 107 | } 108 | 109 | func (th *TransactionHeader) MarshalBinary() ([]byte, error) { 110 | 111 | buf := new(bytes.Buffer) 112 | 113 | buf.Write(helpers.FitBytesInto(th.From, NETWORK_KEY_SIZE)) 114 | buf.Write(helpers.FitBytesInto(th.To, NETWORK_KEY_SIZE)) 115 | binary.Write(buf, binary.LittleEndian, th.Timestamp) 116 | buf.Write(helpers.FitBytesInto(th.PayloadHash, 32)) 117 | binary.Write(buf, binary.LittleEndian, th.PayloadLength) 118 | binary.Write(buf, binary.LittleEndian, th.Nonce) 119 | 120 | return buf.Bytes(), nil 121 | 122 | } 123 | 124 | func (th *TransactionHeader) UnmarshalBinary(d []byte) error { 125 | 126 | buf := bytes.NewBuffer(d) 127 | th.From = helpers.StripByte(buf.Next(NETWORK_KEY_SIZE), 0) 128 | th.To = helpers.StripByte(buf.Next(NETWORK_KEY_SIZE), 0) 129 | binary.Read(bytes.NewBuffer(buf.Next(4)), binary.LittleEndian, &th.Timestamp) 130 | th.PayloadHash = buf.Next(32) 131 | binary.Read(bytes.NewBuffer(buf.Next(4)), binary.LittleEndian, &th.PayloadLength) 132 | binary.Read(bytes.NewBuffer(buf.Next(4)), binary.LittleEndian, &th.Nonce) 133 | 134 | return nil 135 | } 136 | 137 | type TransactionSlice []Transaction 138 | 139 | func (slice TransactionSlice) Len() int { 140 | 141 | return len(slice) 142 | } 143 | 144 | func (slice TransactionSlice) Exists(tr Transaction) bool { 145 | 146 | for _, t := range slice { 147 | if reflect.DeepEqual(t.Signature, tr.Signature) { 148 | return true 149 | } 150 | } 151 | return false 152 | } 153 | 154 | func (slice TransactionSlice) AddTransaction(t Transaction) TransactionSlice { 155 | 156 | // Inserted sorted by timestamp 157 | for i, tr := range slice { 158 | if tr.Header.Timestamp >= t.Header.Timestamp { 159 | return append(append(slice[:i], t), slice[i:]...) 160 | } 161 | } 162 | 163 | return append(slice, t) 164 | } 165 | 166 | func (slice *TransactionSlice) MarshalBinary() ([]byte, error) { 167 | 168 | buf := new(bytes.Buffer) 169 | 170 | for _, t := range *slice { 171 | 172 | bs, err := t.MarshalBinary() 173 | 174 | if err != nil { 175 | return nil, err 176 | } 177 | 178 | buf.Write(bs) 179 | } 180 | 181 | return buf.Bytes(), nil 182 | } 183 | 184 | func (slice *TransactionSlice) UnmarshalBinary(d []byte) error { 185 | 186 | remaining := d 187 | 188 | for len(remaining) > TRANSACTION_HEADER_SIZE+NETWORK_KEY_SIZE { 189 | t := new(Transaction) 190 | rem, err := t.UnmarshalBinary(remaining) 191 | 192 | if err != nil { 193 | return err 194 | } 195 | (*slice) = append((*slice), *t) 196 | remaining = rem 197 | } 198 | return nil 199 | } 200 | -------------------------------------------------------------------------------- /core/transaction_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/izqui/helpers" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestTransactionMarshalling(t *testing.T) { 10 | 11 | kp := GenerateNewKeypair() 12 | tr := NewTransaction(kp.Public, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024*1024)))) 13 | 14 | tr.Header.Nonce = tr.GenerateNonce(helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, TEST_POW_PREFIX)) 15 | tr.Signature = tr.Sign(kp) 16 | 17 | data, err := tr.MarshalBinary() 18 | 19 | if err != nil { 20 | t.Error(err) 21 | } 22 | 23 | newT := &Transaction{} 24 | rem, err := newT.UnmarshalBinary(data) 25 | 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | 30 | if !reflect.DeepEqual(*newT, *tr) || len(rem) < 0 { 31 | t.Error("Marshall, unmarshall failed") 32 | } 33 | } 34 | 35 | func TestTransactionVerification(t *testing.T) { 36 | 37 | pow := helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, TEST_POW_PREFIX) 38 | 39 | kp := GenerateNewKeypair() 40 | tr := NewTransaction(kp.Public, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024)))) 41 | 42 | tr.Header.Nonce = tr.GenerateNonce(pow) 43 | tr.Signature = tr.Sign(kp) 44 | 45 | if !tr.VerifyTransaction(pow) { 46 | 47 | t.Error("Validation failing") 48 | } 49 | } 50 | 51 | func TestIncorrectTransactionPOWVerification(t *testing.T) { 52 | 53 | pow := helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, TEST_POW_PREFIX) 54 | powIncorrect := helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, 'a') 55 | 56 | kp := GenerateNewKeypair() 57 | tr := NewTransaction(kp.Public, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024)))) 58 | tr.Header.Nonce = tr.GenerateNonce(powIncorrect) 59 | tr.Signature = tr.Sign(kp) 60 | 61 | if tr.VerifyTransaction(pow) { 62 | 63 | t.Error("Passed validation without pow") 64 | } 65 | } 66 | 67 | func TestIncorrectTransactionSignatureVerification(t *testing.T) { 68 | 69 | pow := helpers.ArrayOfBytes(TEST_TRANSACTION_POW_COMPLEXITY, TEST_POW_PREFIX) 70 | kp1, kp2 := GenerateNewKeypair(), GenerateNewKeypair() 71 | tr := NewTransaction(kp2.Public, nil, []byte(helpers.RandomString(helpers.RandomInt(0, 1024)))) 72 | tr.Header.Nonce = tr.GenerateNonce(pow) 73 | tr.Signature = tr.Sign(kp1) 74 | 75 | if tr.VerifyTransaction(pow) { 76 | 77 | t.Error("Passed validation with incorrect key") 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /presentation.md: -------------------------------------------------------------------------------- 1 | # The Blockchain 2 | 3 | #### How to build a *distributed**, *immutable*, *tractable* data store 4 | ##### Jorge Izquierdo *-* Jonathan Dahan 5 | 6 | --- 7 | 8 | # **Concepts** Transactions 9 | 10 | ![inline](https://1-ps.googleusercontent.com/s/www.igvita.com/posts/14/xtransaction-pki.png.pagespeed.ic.elb9fXIUMa.png) 11 | 12 | --- 13 | 14 | # **Concepts** Transactions 15 | 16 | ```go 17 | type Transaction struct { 18 | Header TransactionHeader 19 | Signature []byte 20 | Payload []byte 21 | } 22 | 23 | type TransactionHeader struct { 24 | From []byte 25 | To []byte 26 | Timestamp uint32 27 | PayloadHash []byte 28 | PayloadLength uint32 29 | Nonce uint32 30 | } 31 | ``` 32 | 33 | --- 34 | 35 | 36 | # **Concepts** Blocks 37 | 38 | ![inline](https://1-ps.googleusercontent.com/s/www.igvita.com/posts/14/xblockchain-full.png.pagespeed.ic.r5GP2Rwqya.png) 39 | 40 | --- 41 | 42 | # **Concepts** Blocks 43 | 44 | ```go 45 | type Block struct { 46 | *BlockHeader 47 | Signature []byte 48 | *TransactionSlice 49 | } 50 | 51 | type BlockHeader struct { 52 | Origin []byte 53 | PrevBlock []byte 54 | MerkelRoot []byte 55 | Timestamp uint32 56 | Nonce uint32 57 | } 58 | ``` 59 | 60 | --- 61 | 62 | # **Concepts** Proof of work 63 | 64 | ```python 65 | 66 | 67 | 68 | 69 | def proof_of_work(block, difficulty) { 70 | while (block.get_hash()[0:difficulty] != "0" * difficulty): 71 | block.header.nonce += 1 72 | } 73 | ``` 74 | 75 | --- 76 | 77 | # Demo 78 | 79 | #### *[http://github.com/izqui/blockchain](http://github.com/izqui/blockchain)* 80 | 81 | --- 82 | 83 | # **Properties** Decentralized 84 | 85 | * Every peer has to download **all** the data. 86 | * Every peer is connected over **TCP** to as many peers as possible. 87 | * Every new transaction or block is **broadcast** to the network. 88 | 89 | --- 90 | 91 | # **Properties** Inmutability 92 | 93 | * It's really **hard to undo** because Proof of Work is expensive. 94 | * Blocks refer to the hash of the previous block. 95 | * The more blocks built on top of a block, the safer it is. 96 | 97 | 98 | ^ Undoing transaction 3 blocks down requires recalculating all of them 99 | 100 | --- 101 | 102 | # Satoshi's gamble 103 | 104 | * The cost of modifying a previously verified block must be **higher** than the benefit of verifying new blocks. 105 | 106 | * We can now have an append-only, **signed** database that can be completitely **decentralized**. 107 | 108 | --- 109 | 110 | # What can you build on top? 111 | 112 | **Bitcoin** peer-controlled currency 113 | 114 | --- 115 | # What can you build on top? 116 | 117 | **Bitcoin** peer-controlled currency 118 | **Namecoin** distributed DNS service 119 | 120 | --- 121 | 122 | # What can you build on top? 123 | 124 | **Bitcoin** peer-controlled currency 125 | **Namecoin** distributed DNS service 126 | **Certcoin?** web-of-trust style certificate store 127 | 128 | --- 129 | 130 | # What can you build on top? 131 | 132 | **Bitcoin** peer-controlled currency 133 | **Namecoin** distributed DNS service 134 | **Certcoin?** web-of-trust style certificate store 135 | **Keycoin?** distribution of public keys for any identity 136 | 137 | --- 138 | 139 | # What can you build on top? 140 | 141 | **Bitcoin** peer-controlled currency 142 | **Namecoin** distributed DNS service 143 | **Certcoin?** web-of-trust style certificate store 144 | **Keycoin?** distribution of public keys for any identity 145 | **voting systems** digital-suffrage 146 | **file sync** storj.io 147 | **timestamp services** btproof.com proofofexistence.com 148 | **git hosting** gitchain.org 149 | **compute power** https://www.ethereum.org 150 | --- 151 | 152 | ## Join us developing a basic blockchain in which you can **build stuff** and **experiment** on top. 153 | 154 | #### *[http://github.com/izqui/blockchain](http://github.com/izqui/blockchain)* 155 | 156 | --- 157 | 158 | # Thanks! 159 | 160 | ### @jedahan 161 | ### @izqui9 -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/izqui/blockchain/4832a38ed82580572b5cb35b2603713b9926ee16/scripts/build.sh -------------------------------------------------------------------------------- /scripts/docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker build -t izqui/blockchain . 3 | docker run -ti izqui/blockchain 4 | --------------------------------------------------------------------------------