├── .travis.yml ├── LICENSE ├── README.md ├── app_currency.go ├── app_currency.pb.go ├── app_currency.proto ├── app_currency_test.go ├── app_dummy.go ├── app_dummy.pb.go ├── app_dummy.proto ├── app_dummy_test.go ├── app_petition.go ├── app_petition.pb.go ├── app_petition.proto ├── app_petition_test.go ├── app_registrar.go ├── app_registrar.pb.go ├── app_registrar.proto ├── app_registrar_test.go ├── application.go ├── block.go ├── blockchain.go ├── blockstore.go ├── cmd ├── lazyledger_measurements_1 │ ├── .gitignore │ └── main.go ├── lazyledger_measurements_2 │ ├── .gitignore │ └── main.go ├── lazyledger_measurements_3 │ ├── .gitignore │ └── main.go ├── lazyledger_measurements_4 │ ├── .gitignore │ └── main.go ├── lazyledger_measurements_5 │ ├── .gitignore │ └── main.go └── lazyledger_measurements_5d │ ├── .gitignore │ └── main.go ├── constants.go ├── flagger.go ├── flaghasher.go ├── flaghasher_test.go ├── mapstore.go ├── merkletree_exports.go ├── message.go ├── namespacedummyflagger.go ├── probabilisticblock.go ├── probabilisticblock_test.go ├── simpleblock.go ├── simpleblock_test.go ├── todos.txt └── treehashers.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.13.x 4 | before_install: 5 | - go get github.com/mattn/goveralls 6 | script: 7 | - goveralls -v -service=travis-ci 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mustafa Al-Bassam 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lazyledger-prototype 2 | 3 | [![Build Status](https://travis-ci.org/lazyledger/lazyledger-prototype.svg?branch=master)](https://travis-ci.org/lazyledger/lazyledger-prototype) 4 | [![Coverage Status](https://coveralls.io/repos/github/lazyledger/lazyledger-prototype/badge.svg?branch=master)](https://coveralls.io/github/lazyledger/lazyledger-prototype?branch=master) 5 | [![GoDoc](https://godoc.org/github.com/lazyledger/lazyledger-prototype?status.svg)](https://godoc.org/github.com/lazyledger/lazyledger-prototype) 6 | -------------------------------------------------------------------------------- /app_currency.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/golang/protobuf/proto" 7 | "github.com/libp2p/go-libp2p-crypto" 8 | ) 9 | 10 | // Currency is a demo cryptocurrency application. 11 | type Currency struct { 12 | state MapStore 13 | b *Blockchain 14 | transferCallbacks []TransferCallback 15 | } 16 | 17 | type TransferCallback = func(from []byte, to []byte, value int) 18 | 19 | // NewCurrency creates a new currency instance. 20 | func NewCurrency(state MapStore, b *Blockchain) Application { 21 | return &Currency{ 22 | state: state, 23 | b: b, 24 | } 25 | } 26 | 27 | // ProcessMessage processes a message. 28 | func (c *Currency) ProcessMessage(message Message) { 29 | transaction := &CurrencyTransaction{} 30 | err := proto.Unmarshal(message.Data(), transaction) 31 | if err != nil { 32 | return 33 | } 34 | if transaction.Dependency != nil { 35 | block, err := c.b.Block(c.BlockHead()) 36 | if err != nil { 37 | return 38 | } 39 | dependencyProven := block.DependencyProven(transaction.Dependency) 40 | if !dependencyProven { 41 | return 42 | } 43 | } 44 | transactionMessage := &CurrencyTransactionMessage{ 45 | To: transaction.To, 46 | Amount: transaction.Amount, 47 | Dependency: transaction.Dependency, 48 | } 49 | signedData, err := proto.Marshal(transactionMessage) 50 | fromKey, err := crypto.UnmarshalPublicKey(transaction.From) 51 | ok, err := fromKey.Verify(signedData, transaction.Signature) 52 | fromBalanceBytes, err := c.state.Get(transaction.From) 53 | if err != nil { 54 | return 55 | } 56 | fromBalance := binary.BigEndian.Uint64(fromBalanceBytes) 57 | if ok && fromBalance >= *transaction.Amount { 58 | toBalanceBytes, err := c.state.Get(transaction.To) 59 | var toBalance uint64 60 | if err != nil { 61 | toBalance = 0 62 | } else { 63 | toBalance = binary.BigEndian.Uint64(toBalanceBytes) 64 | } 65 | newFromBalanceBytes := make([]byte, binary.MaxVarintLen64) 66 | binary.BigEndian.PutUint64(newFromBalanceBytes, fromBalance - *transaction.Amount) 67 | newToBalanceBytes := make([]byte, binary.MaxVarintLen64) 68 | binary.BigEndian.PutUint64(newToBalanceBytes, toBalance + *transaction.Amount) 69 | 70 | c.state.Put(transaction.From, newFromBalanceBytes) 71 | c.state.Put(transaction.To, newToBalanceBytes) 72 | c.triggerTransferCallbacks(transaction.From, transaction.To, int(*transaction.Amount)) 73 | } 74 | } 75 | 76 | // Namespace returns the application's namespace ID. 77 | func (c *Currency) Namespace() [namespaceSize]byte { 78 | var namespace [namespaceSize]byte 79 | copy(namespace[:], []byte("currency")) 80 | return namespace 81 | } 82 | 83 | // SetBlockHead sets the hash of the latest block that has been processed. 84 | func (c *Currency) SetBlockHead(hash []byte) { 85 | c.state.Put([]byte("__head__"), hash) 86 | } 87 | 88 | // BlockHead returns the hash of the latest block that has been processed. 89 | func (c *Currency) BlockHead() []byte { 90 | head, _ := c.state.Get([]byte("__head__")) 91 | return head 92 | } 93 | 94 | // GenerateTransaction generates a transaction message. 95 | func (c *Currency) GenerateTransaction(fromPrivKey crypto.PrivKey, toPubKey crypto.PubKey, amount uint64, dependency []byte) Message { 96 | toPubKeyBytes, _ := toPubKey.Bytes() 97 | fromPubKeyBytes, _ := fromPrivKey.GetPublic().Bytes() 98 | transactionMessage := &CurrencyTransactionMessage{ 99 | To: toPubKeyBytes, 100 | Amount: &amount, 101 | Dependency: dependency, 102 | } 103 | signedData, _ := proto.Marshal(transactionMessage) 104 | signature, _ := fromPrivKey.Sign(signedData) 105 | transaction := &CurrencyTransaction{ 106 | To: toPubKeyBytes, 107 | From: fromPubKeyBytes, 108 | Amount: &amount, 109 | Signature: signature, 110 | Dependency: dependency, 111 | } 112 | messageData, _ := proto.Marshal(transaction) 113 | return *NewMessage(c.Namespace(), messageData) 114 | } 115 | 116 | // Balance gets the balance of a public key. 117 | func (c *Currency) Balance(pubKey crypto.PubKey) uint64 { 118 | pubKeyBytes, _ := pubKey.Bytes() 119 | balance, err := c.state.Get(pubKeyBytes) 120 | if err != nil { 121 | return 0 122 | } 123 | return binary.BigEndian.Uint64(balance) 124 | } 125 | 126 | func (c *Currency) AddTransferCallback(fn TransferCallback) { 127 | c.transferCallbacks = append(c.transferCallbacks, fn) 128 | } 129 | 130 | func (c *Currency) triggerTransferCallbacks(from []byte, to []byte, value int) { 131 | for _, fn := range c.transferCallbacks { 132 | fn(from, to, value) 133 | } 134 | } 135 | 136 | func (c *Currency) StorageSize() int { 137 | return c.state.storageSize() 138 | } 139 | -------------------------------------------------------------------------------- /app_currency.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: app_currency.proto 3 | 4 | package lazyledger 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | math "math" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the proto package it is being compiled against. 19 | // A compilation error at this line likely means your copy of the 20 | // proto package needs to be updated. 21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 22 | 23 | type CurrencyTransaction struct { 24 | To []byte `protobuf:"bytes,1,req,name=to" json:"to,omitempty"` 25 | From []byte `protobuf:"bytes,2,req,name=from" json:"from,omitempty"` 26 | Amount *uint64 `protobuf:"varint,3,req,name=amount" json:"amount,omitempty"` 27 | Signature []byte `protobuf:"bytes,4,req,name=signature" json:"signature,omitempty"` 28 | Dependency []byte `protobuf:"bytes,5,opt,name=dependency" json:"dependency,omitempty"` 29 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 30 | XXX_unrecognized []byte `json:"-"` 31 | XXX_sizecache int32 `json:"-"` 32 | } 33 | 34 | func (m *CurrencyTransaction) Reset() { *m = CurrencyTransaction{} } 35 | func (m *CurrencyTransaction) String() string { return proto.CompactTextString(m) } 36 | func (*CurrencyTransaction) ProtoMessage() {} 37 | func (*CurrencyTransaction) Descriptor() ([]byte, []int) { 38 | return fileDescriptor_616dce597ab00d2c, []int{0} 39 | } 40 | 41 | func (m *CurrencyTransaction) XXX_Unmarshal(b []byte) error { 42 | return xxx_messageInfo_CurrencyTransaction.Unmarshal(m, b) 43 | } 44 | func (m *CurrencyTransaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 45 | return xxx_messageInfo_CurrencyTransaction.Marshal(b, m, deterministic) 46 | } 47 | func (m *CurrencyTransaction) XXX_Merge(src proto.Message) { 48 | xxx_messageInfo_CurrencyTransaction.Merge(m, src) 49 | } 50 | func (m *CurrencyTransaction) XXX_Size() int { 51 | return xxx_messageInfo_CurrencyTransaction.Size(m) 52 | } 53 | func (m *CurrencyTransaction) XXX_DiscardUnknown() { 54 | xxx_messageInfo_CurrencyTransaction.DiscardUnknown(m) 55 | } 56 | 57 | var xxx_messageInfo_CurrencyTransaction proto.InternalMessageInfo 58 | 59 | func (m *CurrencyTransaction) GetTo() []byte { 60 | if m != nil { 61 | return m.To 62 | } 63 | return nil 64 | } 65 | 66 | func (m *CurrencyTransaction) GetFrom() []byte { 67 | if m != nil { 68 | return m.From 69 | } 70 | return nil 71 | } 72 | 73 | func (m *CurrencyTransaction) GetAmount() uint64 { 74 | if m != nil && m.Amount != nil { 75 | return *m.Amount 76 | } 77 | return 0 78 | } 79 | 80 | func (m *CurrencyTransaction) GetSignature() []byte { 81 | if m != nil { 82 | return m.Signature 83 | } 84 | return nil 85 | } 86 | 87 | func (m *CurrencyTransaction) GetDependency() []byte { 88 | if m != nil { 89 | return m.Dependency 90 | } 91 | return nil 92 | } 93 | 94 | type CurrencyTransactionMessage struct { 95 | To []byte `protobuf:"bytes,1,req,name=to" json:"to,omitempty"` 96 | Amount *uint64 `protobuf:"varint,2,req,name=amount" json:"amount,omitempty"` 97 | Dependency []byte `protobuf:"bytes,3,opt,name=dependency" json:"dependency,omitempty"` 98 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 99 | XXX_unrecognized []byte `json:"-"` 100 | XXX_sizecache int32 `json:"-"` 101 | } 102 | 103 | func (m *CurrencyTransactionMessage) Reset() { *m = CurrencyTransactionMessage{} } 104 | func (m *CurrencyTransactionMessage) String() string { return proto.CompactTextString(m) } 105 | func (*CurrencyTransactionMessage) ProtoMessage() {} 106 | func (*CurrencyTransactionMessage) Descriptor() ([]byte, []int) { 107 | return fileDescriptor_616dce597ab00d2c, []int{1} 108 | } 109 | 110 | func (m *CurrencyTransactionMessage) XXX_Unmarshal(b []byte) error { 111 | return xxx_messageInfo_CurrencyTransactionMessage.Unmarshal(m, b) 112 | } 113 | func (m *CurrencyTransactionMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 114 | return xxx_messageInfo_CurrencyTransactionMessage.Marshal(b, m, deterministic) 115 | } 116 | func (m *CurrencyTransactionMessage) XXX_Merge(src proto.Message) { 117 | xxx_messageInfo_CurrencyTransactionMessage.Merge(m, src) 118 | } 119 | func (m *CurrencyTransactionMessage) XXX_Size() int { 120 | return xxx_messageInfo_CurrencyTransactionMessage.Size(m) 121 | } 122 | func (m *CurrencyTransactionMessage) XXX_DiscardUnknown() { 123 | xxx_messageInfo_CurrencyTransactionMessage.DiscardUnknown(m) 124 | } 125 | 126 | var xxx_messageInfo_CurrencyTransactionMessage proto.InternalMessageInfo 127 | 128 | func (m *CurrencyTransactionMessage) GetTo() []byte { 129 | if m != nil { 130 | return m.To 131 | } 132 | return nil 133 | } 134 | 135 | func (m *CurrencyTransactionMessage) GetAmount() uint64 { 136 | if m != nil && m.Amount != nil { 137 | return *m.Amount 138 | } 139 | return 0 140 | } 141 | 142 | func (m *CurrencyTransactionMessage) GetDependency() []byte { 143 | if m != nil { 144 | return m.Dependency 145 | } 146 | return nil 147 | } 148 | 149 | func init() { 150 | proto.RegisterType((*CurrencyTransaction)(nil), "lazyledger.CurrencyTransaction") 151 | proto.RegisterType((*CurrencyTransactionMessage)(nil), "lazyledger.CurrencyTransactionMessage") 152 | } 153 | 154 | func init() { proto.RegisterFile("app_currency.proto", fileDescriptor_616dce597ab00d2c) } 155 | 156 | var fileDescriptor_616dce597ab00d2c = []byte{ 157 | // 187 bytes of a gzipped FileDescriptorProto 158 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x8e, 0xc1, 0xca, 0x82, 0x40, 159 | 0x10, 0x80, 0x71, 0xf5, 0xff, 0xa1, 0x41, 0x3a, 0x4c, 0x10, 0x4b, 0x44, 0x88, 0x27, 0x4f, 0xbd, 160 | 0x44, 0xe7, 0x2e, 0xd2, 0x3d, 0x16, 0x77, 0x12, 0x41, 0x77, 0x97, 0xd9, 0xf5, 0x60, 0x2f, 0xd1, 161 | 0x2b, 0x47, 0x22, 0x98, 0xd5, 0x6d, 0xe6, 0x1b, 0x86, 0xef, 0x03, 0x54, 0xce, 0x5d, 0xab, 0x9e, 162 | 0x99, 0x4c, 0x35, 0x1c, 0x1d, 0xdb, 0x60, 0x11, 0x5a, 0x75, 0x1f, 0x5a, 0xd2, 0x35, 0x71, 0xfe, 163 | 0x88, 0x60, 0x73, 0x9a, 0xce, 0x17, 0x56, 0xc6, 0xab, 0x2a, 0x34, 0xd6, 0xe0, 0x1a, 0x44, 0xb0, 164 | 0x32, 0xca, 0x44, 0x91, 0x96, 0x22, 0x58, 0x44, 0x48, 0x6e, 0x6c, 0x3b, 0x29, 0x46, 0x32, 0xce, 165 | 0xb8, 0x85, 0x7f, 0xd5, 0xd9, 0xde, 0x04, 0x19, 0x67, 0xa2, 0x48, 0xca, 0x69, 0xc3, 0x3d, 0xac, 166 | 0x7c, 0x53, 0x1b, 0x15, 0x7a, 0x26, 0x99, 0x8c, 0x0f, 0x33, 0xc0, 0x03, 0x80, 0x26, 0x47, 0x46, 167 | 0xbf, 0x94, 0xf2, 0x2f, 0x8b, 0x8a, 0xb4, 0x7c, 0x23, 0xb9, 0x86, 0xdd, 0x8f, 0xa0, 0x33, 0x79, 168 | 0xaf, 0x6a, 0xfa, 0xea, 0x9a, 0x1b, 0xc4, 0xa2, 0x61, 0x69, 0x89, 0x3f, 0x2d, 0xcf, 0x00, 0x00, 169 | 0x00, 0xff, 0xff, 0x7d, 0x86, 0xba, 0x4f, 0x18, 0x01, 0x00, 0x00, 170 | } 171 | -------------------------------------------------------------------------------- /app_currency.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package lazyledger; 3 | 4 | message CurrencyTransaction { 5 | required bytes to = 1; 6 | required bytes from = 2; 7 | required uint64 amount = 3; 8 | required bytes signature = 4; 9 | optional bytes dependency = 5; 10 | } 11 | 12 | message CurrencyTransactionMessage { 13 | required bytes to = 1; 14 | required uint64 amount = 2; 15 | optional bytes dependency = 3; 16 | } 17 | -------------------------------------------------------------------------------- /app_currency_test.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "testing" 7 | 8 | "github.com/libp2p/go-libp2p-crypto" 9 | ) 10 | 11 | func TestAppCurrencySimpleBlock(t *testing.T) { 12 | bs := NewSimpleBlockStore() 13 | b := NewBlockchain(bs) 14 | 15 | sb := NewSimpleBlock([]byte{0}) 16 | 17 | ms := NewSimpleMap() 18 | app := NewCurrency(ms, b) 19 | b.RegisterApplication(&app) 20 | 21 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 22 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 23 | pubABytes, _ := pubA.Bytes() 24 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 25 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000) 26 | ms.Put(pubABytes, pubABalanceBytes) 27 | 28 | sb.AddMessage(app.(*Currency).GenerateTransaction(privA, pubB, 100, nil)) 29 | b.ProcessBlock(sb) 30 | 31 | if app.(*Currency).Balance(pubA) != 900 || app.(*Currency).Balance(pubB) != 100 { 32 | t.Error("test tranasaction failed: invalid post-balances") 33 | } 34 | } 35 | 36 | func TestAppCurrencySimpleBlockDependency(t *testing.T) { 37 | bs := NewSimpleBlockStore() 38 | b := NewBlockchain(bs) 39 | 40 | sb := NewSimpleBlock([]byte{0}) 41 | 42 | ms := NewSimpleMap() 43 | app := NewCurrency(ms, b) 44 | b.RegisterApplication(&app) 45 | 46 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 47 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 48 | pubABytes, _ := pubA.Bytes() 49 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 50 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000) 51 | ms.Put(pubABytes, pubABalanceBytes) 52 | 53 | sb.AddMessage(*NewMessage([namespaceSize]byte{0}, []byte("foo"))) 54 | hash, _, _ := sb.ProveDependency(0) 55 | sb.AddMessage(app.(*Currency).GenerateTransaction(privA, pubB, 100, hash)) 56 | _, proof, _ := sb.ProveDependency(0) 57 | sb.VerifyDependency(0, hash, proof) 58 | b.ProcessBlock(sb) 59 | 60 | if app.(*Currency).Balance(pubA) != 900 || app.(*Currency).Balance(pubB) != 100 { 61 | t.Error("test tranasaction failed: invalid post-balances") 62 | } 63 | } 64 | 65 | func TestAppCurrencyProbabilisticBlockDependency(t *testing.T) { 66 | bs := NewSimpleBlockStore() 67 | b := NewBlockchain(bs) 68 | 69 | pb := NewProbabilisticBlock([]byte{0}, 512) 70 | 71 | ms := NewSimpleMap() 72 | app := NewCurrency(ms, b) 73 | b.RegisterApplication(&app) 74 | 75 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 76 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 77 | pubABytes, _ := pubA.Bytes() 78 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 79 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000) 80 | ms.Put(pubABytes, pubABalanceBytes) 81 | 82 | pb.AddMessage(*NewMessage([namespaceSize]byte{0}, []byte("foo"))) 83 | hash, _, _ := pb.ProveDependency(0) 84 | pb.AddMessage(app.(*Currency).GenerateTransaction(privA, pubB, 100, hash)) 85 | _, proof, _ := pb.ProveDependency(0) 86 | pb.VerifyDependency(0, hash, proof) 87 | b.ProcessBlock(pb) 88 | 89 | if app.(*Currency).Balance(pubA) != 900 || app.(*Currency).Balance(pubB) != 100 { 90 | t.Error("test tranasaction failed: invalid post-balances") 91 | } 92 | } 93 | 94 | func TestAppCurrencyProbabilisticBlock(t *testing.T) { 95 | bs := NewSimpleBlockStore() 96 | b := NewBlockchain(bs) 97 | 98 | pb := NewProbabilisticBlock([]byte{0}, 512) 99 | 100 | ms := NewSimpleMap() 101 | app := NewCurrency(ms, b) 102 | b.RegisterApplication(&app) 103 | 104 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 105 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 106 | pubABytes, _ := pubA.Bytes() 107 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 108 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000) 109 | ms.Put(pubABytes, pubABalanceBytes) 110 | 111 | pb.AddMessage(app.(*Currency).GenerateTransaction(privA, pubB, 100, nil)) 112 | b.ProcessBlock(pb) 113 | 114 | if app.(*Currency).Balance(pubA) != 900 || app.(*Currency).Balance(pubB) != 100 { 115 | t.Error("test tranasaction failed: invalid post-balances") 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app_dummy.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | ) 6 | 7 | type DummyApp struct { 8 | state MapStore 9 | } 10 | 11 | func NewDummyApp(state MapStore) Application { 12 | return &DummyApp{ 13 | state: state, 14 | } 15 | } 16 | 17 | func (app *DummyApp) ProcessMessage(message Message) { 18 | transaction := &DummyAppTransaction{} 19 | err := proto.Unmarshal(message.Data(), transaction) 20 | if err != nil { 21 | return 22 | } 23 | for k, v := range transaction.Puts { 24 | app.state.Put([]byte(k), []byte(v)) 25 | } 26 | } 27 | 28 | func (app *DummyApp) Namespace() [namespaceSize]byte { 29 | var namespace [namespaceSize]byte 30 | copy(namespace[:], []byte("dummy")) 31 | return namespace 32 | } 33 | 34 | func (app *DummyApp) SetBlockHead(hash []byte) { 35 | app.state.Put([]byte("__head__"), hash) 36 | } 37 | 38 | func (app *DummyApp) BlockHead() []byte { 39 | head, _ := app.state.Get([]byte("__head__")) 40 | return head 41 | } 42 | 43 | func (app *DummyApp) Get(key string) string { 44 | value, err := app.state.Get([]byte(key)) 45 | if err != nil { 46 | return "" 47 | } 48 | return string(value) 49 | } 50 | 51 | func (app *DummyApp) GenerateTransaction(puts map[string]string) Message { 52 | transaction := &DummyAppTransaction{ 53 | Puts: puts, 54 | } 55 | data, _ := proto.Marshal(transaction) 56 | return *NewMessage(app.Namespace(), data) 57 | } 58 | 59 | func (app *DummyApp) StorageSize() int { 60 | return app.state.storageSize() 61 | } 62 | -------------------------------------------------------------------------------- /app_dummy.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: app_dummy.proto 3 | 4 | package lazyledger 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | math "math" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the proto package it is being compiled against. 19 | // A compilation error at this line likely means your copy of the 20 | // proto package needs to be updated. 21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 22 | 23 | type DummyAppTransaction struct { 24 | Puts map[string]string `protobuf:"bytes,1,rep,name=puts" json:"puts,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` 25 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 26 | XXX_unrecognized []byte `json:"-"` 27 | XXX_sizecache int32 `json:"-"` 28 | } 29 | 30 | func (m *DummyAppTransaction) Reset() { *m = DummyAppTransaction{} } 31 | func (m *DummyAppTransaction) String() string { return proto.CompactTextString(m) } 32 | func (*DummyAppTransaction) ProtoMessage() {} 33 | func (*DummyAppTransaction) Descriptor() ([]byte, []int) { 34 | return fileDescriptor_6fabccd89524743b, []int{0} 35 | } 36 | 37 | func (m *DummyAppTransaction) XXX_Unmarshal(b []byte) error { 38 | return xxx_messageInfo_DummyAppTransaction.Unmarshal(m, b) 39 | } 40 | func (m *DummyAppTransaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 41 | return xxx_messageInfo_DummyAppTransaction.Marshal(b, m, deterministic) 42 | } 43 | func (m *DummyAppTransaction) XXX_Merge(src proto.Message) { 44 | xxx_messageInfo_DummyAppTransaction.Merge(m, src) 45 | } 46 | func (m *DummyAppTransaction) XXX_Size() int { 47 | return xxx_messageInfo_DummyAppTransaction.Size(m) 48 | } 49 | func (m *DummyAppTransaction) XXX_DiscardUnknown() { 50 | xxx_messageInfo_DummyAppTransaction.DiscardUnknown(m) 51 | } 52 | 53 | var xxx_messageInfo_DummyAppTransaction proto.InternalMessageInfo 54 | 55 | func (m *DummyAppTransaction) GetPuts() map[string]string { 56 | if m != nil { 57 | return m.Puts 58 | } 59 | return nil 60 | } 61 | 62 | func init() { 63 | proto.RegisterType((*DummyAppTransaction)(nil), "lazyledger.DummyAppTransaction") 64 | proto.RegisterMapType((map[string]string)(nil), "lazyledger.DummyAppTransaction.PutsEntry") 65 | } 66 | 67 | func init() { proto.RegisterFile("app_dummy.proto", fileDescriptor_6fabccd89524743b) } 68 | 69 | var fileDescriptor_6fabccd89524743b = []byte{ 70 | // 150 bytes of a gzipped FileDescriptorProto 71 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4f, 0x2c, 0x28, 0x88, 72 | 0x4f, 0x29, 0xcd, 0xcd, 0xad, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0xca, 0x49, 0xac, 73 | 0xaa, 0xcc, 0x49, 0x4d, 0x49, 0x4f, 0x2d, 0x52, 0xea, 0x65, 0xe4, 0x12, 0x76, 0x01, 0xc9, 0x39, 74 | 0x16, 0x14, 0x84, 0x14, 0x25, 0xe6, 0x15, 0x27, 0x26, 0x97, 0x64, 0xe6, 0xe7, 0x09, 0xd9, 0x72, 75 | 0xb1, 0x14, 0x94, 0x96, 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x1b, 0x69, 0xea, 0x21, 0xb4, 76 | 0xe8, 0x61, 0x51, 0xae, 0x17, 0x50, 0x5a, 0x52, 0xec, 0x9a, 0x57, 0x52, 0x54, 0x19, 0x04, 0xd6, 77 | 0x26, 0x65, 0xce, 0xc5, 0x09, 0x17, 0x12, 0x12, 0xe0, 0x62, 0xce, 0x4e, 0xad, 0x94, 0x60, 0x54, 78 | 0x60, 0xd4, 0xe0, 0x0c, 0x02, 0x31, 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x12, 0x73, 0x4a, 0x53, 0x25, 79 | 0x98, 0xc0, 0x62, 0x10, 0x8e, 0x15, 0x93, 0x05, 0x23, 0x20, 0x00, 0x00, 0xff, 0xff, 0xa7, 0xfd, 80 | 0x97, 0xf6, 0xad, 0x00, 0x00, 0x00, 81 | } 82 | -------------------------------------------------------------------------------- /app_dummy.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package lazyledger; 3 | 4 | message DummyAppTransaction { 5 | map puts = 1; 6 | } 7 | -------------------------------------------------------------------------------- /app_dummy_test.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | 8 | func TestAppDummySimpleBlock(t *testing.T) { 9 | bs := NewSimpleBlockStore() 10 | b := NewBlockchain(bs) 11 | 12 | sb := NewSimpleBlock([]byte{0}) 13 | 14 | ms := NewSimpleMap() 15 | app := NewDummyApp(ms) 16 | b.RegisterApplication(&app) 17 | 18 | puts := make(map[string]string) 19 | puts["foo"] = "bar" 20 | puts["goo"] = "tar" 21 | 22 | sb.AddMessage(app.(*DummyApp).GenerateTransaction(puts)) 23 | b.ProcessBlock(sb) 24 | 25 | if app.(*DummyApp).Get("foo") != "bar" || app.(*DummyApp).Get("goo") != "tar" { 26 | t.Error("dummy app state update failed") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app_petition.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "github.com/golang/protobuf/proto" 7 | "github.com/libp2p/go-libp2p-crypto" 8 | ) 9 | 10 | type PetitionApp struct { 11 | state MapStore 12 | } 13 | 14 | func NewPetitionApp(state MapStore) Application { 15 | return &PetitionApp{ 16 | state: state, 17 | } 18 | } 19 | 20 | func (app *PetitionApp) ProcessMessage(message Message) { 21 | transaction := &PetitionAppTransaction{} 22 | err := proto.Unmarshal(message.Data(), transaction) 23 | if err != nil { 24 | return 25 | } 26 | apm := transaction.GetApm() 27 | if apm != nil { 28 | app.ProcessAddPetitionMessage(apm) 29 | } 30 | spm := transaction.GetSpm() 31 | if spm != nil { 32 | app.ProcessSignPetitionMessage(spm) 33 | } 34 | } 35 | 36 | func (app *PetitionApp) ProcessAddPetitionMessage(apm *AddPetitionMessage) { 37 | app.addPetition(*apm.Text) 38 | } 39 | 40 | func (app *PetitionApp) ProcessSignPetitionMessage(spm *SignPetitionMessage) { 41 | key, _ := crypto.UnmarshalPublicKey(spm.Signer) 42 | signedData := make([]byte, binary.MaxVarintLen64) 43 | binary.BigEndian.PutUint64(signedData, *spm.Id) 44 | ok, err := key.Verify(signedData, spm.Signature) 45 | if !ok || err != nil { 46 | return 47 | } 48 | app.incrementPetition(*spm.Id) 49 | } 50 | 51 | func (app *PetitionApp) Namespace() [namespaceSize]byte { 52 | var namespace [namespaceSize]byte 53 | copy(namespace[:], []byte("pet")) 54 | return namespace 55 | } 56 | 57 | func (app *PetitionApp) SetBlockHead(hash []byte) { 58 | app.state.Put([]byte("__head__"), hash) 59 | } 60 | 61 | func (app *PetitionApp) BlockHead() []byte { 62 | head, _ := app.state.Get([]byte("__head__")) 63 | return head 64 | } 65 | 66 | func (app *PetitionApp) Petition(petition uint64) uint64 { 67 | id := make([]byte, binary.MaxVarintLen64) 68 | binary.BigEndian.PutUint64(id, petition) 69 | c, err := app.state.Get(id) 70 | if err != nil { 71 | return 0 72 | } 73 | return binary.BigEndian.Uint64(c) 74 | } 75 | 76 | func (app *PetitionApp) incrementPetition(petition uint64) { 77 | v := app.Petition(petition) 78 | newValue := make([]byte, binary.MaxVarintLen64) 79 | binary.BigEndian.PutUint64(newValue, v + 1) 80 | 81 | id := make([]byte, binary.MaxVarintLen64) 82 | binary.BigEndian.PutUint64(id, petition) 83 | app.state.Put(id, newValue) 84 | } 85 | 86 | func (app *PetitionApp) addPetition(text string) { 87 | latestIdBytes, err := app.state.Get([]byte("__last__")) 88 | var latestId uint64 89 | if err != nil { 90 | latestId = 0 91 | } else { 92 | latestId = binary.BigEndian.Uint64(latestIdBytes) 93 | } 94 | newValue := make([]byte, binary.MaxVarintLen64) 95 | binary.BigEndian.PutUint64(newValue, latestId + 1) 96 | app.state.Put([]byte("__last__"), newValue) 97 | app.state.Put(append([]byte("text__"), newValue...), []byte(text)) 98 | } 99 | 100 | func (app *PetitionApp) GenerateSignPetitionTransaction(key crypto.PrivKey, petition uint64) Message { 101 | signedData := make([]byte, binary.MaxVarintLen64) 102 | binary.BigEndian.PutUint64(signedData, petition) 103 | sig, _ := key.Sign(signedData) 104 | kb, _ := key.GetPublic().Bytes() 105 | spm := &SignPetitionMessage{ 106 | Id: &petition, 107 | Signature: sig, 108 | Signer: kb, 109 | } 110 | pspm := PetitionAppTransaction_Spm{Spm: spm} 111 | t := &PetitionAppTransaction{ 112 | Message: &pspm, 113 | } 114 | d, _ := proto.Marshal(t) 115 | return *NewMessage(app.Namespace(), d) 116 | } 117 | 118 | func (app *PetitionApp) GenerateAddPetitionTransaction(text string) Message { 119 | apm := &AddPetitionMessage{ 120 | Text: &text, 121 | } 122 | papm := PetitionAppTransaction_Apm{Apm: apm} 123 | t := &PetitionAppTransaction{ 124 | Message: &papm, 125 | } 126 | d, _ := proto.Marshal(t) 127 | return *NewMessage(app.Namespace(), d) 128 | } 129 | -------------------------------------------------------------------------------- /app_petition.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: app_petition.proto 3 | 4 | package lazyledger 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | math "math" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the proto package it is being compiled against. 19 | // A compilation error at this line likely means your copy of the 20 | // proto package needs to be updated. 21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 22 | 23 | type PetitionAppTransaction struct { 24 | // Types that are valid to be assigned to Message: 25 | // *PetitionAppTransaction_Apm 26 | // *PetitionAppTransaction_Spm 27 | Message isPetitionAppTransaction_Message `protobuf_oneof:"message"` 28 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 29 | XXX_unrecognized []byte `json:"-"` 30 | XXX_sizecache int32 `json:"-"` 31 | } 32 | 33 | func (m *PetitionAppTransaction) Reset() { *m = PetitionAppTransaction{} } 34 | func (m *PetitionAppTransaction) String() string { return proto.CompactTextString(m) } 35 | func (*PetitionAppTransaction) ProtoMessage() {} 36 | func (*PetitionAppTransaction) Descriptor() ([]byte, []int) { 37 | return fileDescriptor_e792406857c1b4a0, []int{0} 38 | } 39 | 40 | func (m *PetitionAppTransaction) XXX_Unmarshal(b []byte) error { 41 | return xxx_messageInfo_PetitionAppTransaction.Unmarshal(m, b) 42 | } 43 | func (m *PetitionAppTransaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 44 | return xxx_messageInfo_PetitionAppTransaction.Marshal(b, m, deterministic) 45 | } 46 | func (m *PetitionAppTransaction) XXX_Merge(src proto.Message) { 47 | xxx_messageInfo_PetitionAppTransaction.Merge(m, src) 48 | } 49 | func (m *PetitionAppTransaction) XXX_Size() int { 50 | return xxx_messageInfo_PetitionAppTransaction.Size(m) 51 | } 52 | func (m *PetitionAppTransaction) XXX_DiscardUnknown() { 53 | xxx_messageInfo_PetitionAppTransaction.DiscardUnknown(m) 54 | } 55 | 56 | var xxx_messageInfo_PetitionAppTransaction proto.InternalMessageInfo 57 | 58 | type isPetitionAppTransaction_Message interface { 59 | isPetitionAppTransaction_Message() 60 | } 61 | 62 | type PetitionAppTransaction_Apm struct { 63 | Apm *AddPetitionMessage `protobuf:"bytes,1,opt,name=apm,oneof"` 64 | } 65 | 66 | type PetitionAppTransaction_Spm struct { 67 | Spm *SignPetitionMessage `protobuf:"bytes,2,opt,name=spm,oneof"` 68 | } 69 | 70 | func (*PetitionAppTransaction_Apm) isPetitionAppTransaction_Message() {} 71 | 72 | func (*PetitionAppTransaction_Spm) isPetitionAppTransaction_Message() {} 73 | 74 | func (m *PetitionAppTransaction) GetMessage() isPetitionAppTransaction_Message { 75 | if m != nil { 76 | return m.Message 77 | } 78 | return nil 79 | } 80 | 81 | func (m *PetitionAppTransaction) GetApm() *AddPetitionMessage { 82 | if x, ok := m.GetMessage().(*PetitionAppTransaction_Apm); ok { 83 | return x.Apm 84 | } 85 | return nil 86 | } 87 | 88 | func (m *PetitionAppTransaction) GetSpm() *SignPetitionMessage { 89 | if x, ok := m.GetMessage().(*PetitionAppTransaction_Spm); ok { 90 | return x.Spm 91 | } 92 | return nil 93 | } 94 | 95 | // XXX_OneofWrappers is for the internal use of the proto package. 96 | func (*PetitionAppTransaction) XXX_OneofWrappers() []interface{} { 97 | return []interface{}{ 98 | (*PetitionAppTransaction_Apm)(nil), 99 | (*PetitionAppTransaction_Spm)(nil), 100 | } 101 | } 102 | 103 | type AddPetitionMessage struct { 104 | Text *string `protobuf:"bytes,1,req,name=text" json:"text,omitempty"` 105 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 106 | XXX_unrecognized []byte `json:"-"` 107 | XXX_sizecache int32 `json:"-"` 108 | } 109 | 110 | func (m *AddPetitionMessage) Reset() { *m = AddPetitionMessage{} } 111 | func (m *AddPetitionMessage) String() string { return proto.CompactTextString(m) } 112 | func (*AddPetitionMessage) ProtoMessage() {} 113 | func (*AddPetitionMessage) Descriptor() ([]byte, []int) { 114 | return fileDescriptor_e792406857c1b4a0, []int{1} 115 | } 116 | 117 | func (m *AddPetitionMessage) XXX_Unmarshal(b []byte) error { 118 | return xxx_messageInfo_AddPetitionMessage.Unmarshal(m, b) 119 | } 120 | func (m *AddPetitionMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 121 | return xxx_messageInfo_AddPetitionMessage.Marshal(b, m, deterministic) 122 | } 123 | func (m *AddPetitionMessage) XXX_Merge(src proto.Message) { 124 | xxx_messageInfo_AddPetitionMessage.Merge(m, src) 125 | } 126 | func (m *AddPetitionMessage) XXX_Size() int { 127 | return xxx_messageInfo_AddPetitionMessage.Size(m) 128 | } 129 | func (m *AddPetitionMessage) XXX_DiscardUnknown() { 130 | xxx_messageInfo_AddPetitionMessage.DiscardUnknown(m) 131 | } 132 | 133 | var xxx_messageInfo_AddPetitionMessage proto.InternalMessageInfo 134 | 135 | func (m *AddPetitionMessage) GetText() string { 136 | if m != nil && m.Text != nil { 137 | return *m.Text 138 | } 139 | return "" 140 | } 141 | 142 | type SignPetitionMessage struct { 143 | Id *uint64 `protobuf:"varint,1,req,name=id" json:"id,omitempty"` 144 | Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"` 145 | Signer []byte `protobuf:"bytes,3,req,name=signer" json:"signer,omitempty"` 146 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 147 | XXX_unrecognized []byte `json:"-"` 148 | XXX_sizecache int32 `json:"-"` 149 | } 150 | 151 | func (m *SignPetitionMessage) Reset() { *m = SignPetitionMessage{} } 152 | func (m *SignPetitionMessage) String() string { return proto.CompactTextString(m) } 153 | func (*SignPetitionMessage) ProtoMessage() {} 154 | func (*SignPetitionMessage) Descriptor() ([]byte, []int) { 155 | return fileDescriptor_e792406857c1b4a0, []int{2} 156 | } 157 | 158 | func (m *SignPetitionMessage) XXX_Unmarshal(b []byte) error { 159 | return xxx_messageInfo_SignPetitionMessage.Unmarshal(m, b) 160 | } 161 | func (m *SignPetitionMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 162 | return xxx_messageInfo_SignPetitionMessage.Marshal(b, m, deterministic) 163 | } 164 | func (m *SignPetitionMessage) XXX_Merge(src proto.Message) { 165 | xxx_messageInfo_SignPetitionMessage.Merge(m, src) 166 | } 167 | func (m *SignPetitionMessage) XXX_Size() int { 168 | return xxx_messageInfo_SignPetitionMessage.Size(m) 169 | } 170 | func (m *SignPetitionMessage) XXX_DiscardUnknown() { 171 | xxx_messageInfo_SignPetitionMessage.DiscardUnknown(m) 172 | } 173 | 174 | var xxx_messageInfo_SignPetitionMessage proto.InternalMessageInfo 175 | 176 | func (m *SignPetitionMessage) GetId() uint64 { 177 | if m != nil && m.Id != nil { 178 | return *m.Id 179 | } 180 | return 0 181 | } 182 | 183 | func (m *SignPetitionMessage) GetSignature() []byte { 184 | if m != nil { 185 | return m.Signature 186 | } 187 | return nil 188 | } 189 | 190 | func (m *SignPetitionMessage) GetSigner() []byte { 191 | if m != nil { 192 | return m.Signer 193 | } 194 | return nil 195 | } 196 | 197 | func init() { 198 | proto.RegisterType((*PetitionAppTransaction)(nil), "lazyledger.PetitionAppTransaction") 199 | proto.RegisterType((*AddPetitionMessage)(nil), "lazyledger.AddPetitionMessage") 200 | proto.RegisterType((*SignPetitionMessage)(nil), "lazyledger.SignPetitionMessage") 201 | } 202 | 203 | func init() { proto.RegisterFile("app_petition.proto", fileDescriptor_e792406857c1b4a0) } 204 | 205 | var fileDescriptor_e792406857c1b4a0 = []byte{ 206 | // 216 bytes of a gzipped FileDescriptorProto 207 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x8c, 0xb1, 0x4a, 0x04, 0x31, 208 | 0x10, 0x86, 0xdd, 0xec, 0xa1, 0xec, 0x28, 0x16, 0x23, 0x1c, 0x29, 0x44, 0x97, 0xad, 0x52, 0x6d, 209 | 0x71, 0x3e, 0xc1, 0x59, 0xd9, 0x08, 0x12, 0xed, 0x2c, 0x24, 0x98, 0x21, 0x04, 0x2e, 0xd9, 0x21, 210 | 0x89, 0xa0, 0x3e, 0x83, 0x0f, 0x2d, 0x1b, 0x4f, 0x4e, 0x70, 0xbb, 0xf9, 0xe6, 0xff, 0xbf, 0x1f, 211 | 0xd0, 0x30, 0xbf, 0x30, 0x15, 0x5f, 0xfc, 0x14, 0x47, 0x4e, 0x53, 0x99, 0x10, 0x76, 0xe6, 0xf3, 212 | 0x63, 0x47, 0xd6, 0x51, 0x1a, 0xbe, 0x1a, 0x58, 0x3f, 0xec, 0xe3, 0x2d, 0xf3, 0x53, 0x32, 0x31, 213 | 0x9b, 0xd7, 0x99, 0x70, 0x03, 0xad, 0xe1, 0x20, 0x9b, 0xbe, 0x51, 0xa7, 0x9b, 0xab, 0xf1, 0x20, 214 | 0x8d, 0x5b, 0x6b, 0x7f, 0x9d, 0x7b, 0xca, 0xd9, 0x38, 0xba, 0x3b, 0xd2, 0x73, 0x19, 0x6f, 0xa0, 215 | 0xcd, 0x1c, 0xa4, 0xa8, 0xce, 0xf5, 0x5f, 0xe7, 0xd1, 0xbb, 0xb8, 0x20, 0x65, 0x0e, 0xb7, 0x1d, 216 | 0x9c, 0x84, 0x9f, 0xcf, 0xa0, 0x00, 0xff, 0x8f, 0x23, 0xc2, 0xaa, 0xd0, 0x7b, 0x91, 0x4d, 0x2f, 217 | 0x54, 0xa7, 0xeb, 0x3d, 0x3c, 0xc3, 0xc5, 0xc2, 0x24, 0x9e, 0x83, 0xf0, 0xb6, 0x16, 0x57, 0x5a, 218 | 0x78, 0x8b, 0x97, 0xd0, 0x65, 0xef, 0xa2, 0x29, 0x6f, 0x89, 0xa4, 0xe8, 0x85, 0x3a, 0xd3, 0x87, 219 | 0x07, 0xae, 0xe1, 0x78, 0x06, 0x4a, 0xb2, 0xad, 0xd1, 0x9e, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 220 | 0x85, 0x47, 0xe6, 0xf1, 0x36, 0x01, 0x00, 0x00, 221 | } 222 | -------------------------------------------------------------------------------- /app_petition.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package lazyledger; 3 | 4 | message PetitionAppTransaction { 5 | oneof message { 6 | AddPetitionMessage apm = 1; 7 | SignPetitionMessage spm = 2; 8 | } 9 | } 10 | 11 | message AddPetitionMessage { 12 | required string text = 1; 13 | } 14 | 15 | message SignPetitionMessage { 16 | required uint64 id = 1; 17 | required bytes signature = 2; 18 | required bytes signer = 3; 19 | } 20 | -------------------------------------------------------------------------------- /app_petition_test.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "crypto/rand" 5 | "testing" 6 | 7 | "github.com/libp2p/go-libp2p-crypto" 8 | ) 9 | 10 | func TestAppPetitionSimpleBlock(t *testing.T) { 11 | bs := NewSimpleBlockStore() 12 | b := NewBlockchain(bs) 13 | 14 | sb := NewSimpleBlock([]byte{0}) 15 | 16 | ms := NewSimpleMap() 17 | app := NewPetitionApp(ms) 18 | b.RegisterApplication(&app) 19 | 20 | privA, _, _ := crypto.GenerateSecp256k1Key(rand.Reader) 21 | 22 | sb.AddMessage(app.(*PetitionApp).GenerateAddPetitionTransaction("foo")) 23 | sb.AddMessage(app.(*PetitionApp).GenerateSignPetitionTransaction(privA, 0)) 24 | b.ProcessBlock(sb) 25 | 26 | if app.(*PetitionApp).Petition(0) != 1 { 27 | t.Error("failed to sign petition") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app_registrar.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "encoding/binary" 5 | "bytes" 6 | 7 | "github.com/golang/protobuf/proto" 8 | "github.com/libp2p/go-libp2p-crypto" 9 | ) 10 | 11 | type Registrar struct { 12 | state MapStore 13 | currency *Currency 14 | owner []byte 15 | namespace [namespaceSize]byte 16 | } 17 | 18 | func NewRegistrar(state MapStore, currency *Currency, owner []byte) Application { 19 | app := &Registrar{ 20 | state: state, 21 | currency: currency, 22 | owner: owner, 23 | } 24 | currency.AddTransferCallback(app.transferCallback) 25 | return app 26 | } 27 | 28 | func (app *Registrar) ProcessMessage(message Message) { 29 | transaction := &RegisterTransaction{} 30 | err := proto.Unmarshal(message.Data(), transaction) 31 | if err != nil { 32 | return 33 | } 34 | transactionMessage := &RegisterTransactionMessage{ 35 | Name: transaction.Name, 36 | } 37 | signedData, err := proto.Marshal(transactionMessage) 38 | ownerKey, err := crypto.UnmarshalPublicKey(transaction.Owner) 39 | ok, err := ownerKey.Verify(signedData, transaction.Signature) 40 | if !ok { 41 | return 42 | } 43 | if bytes.Compare(app.Name(transaction.Name), []byte{}) != 0 { // check name is available 44 | return 45 | } 46 | 47 | // check and subtract balance 48 | balance := app.Balance(transaction.Owner) 49 | if balance < 5 { 50 | return 51 | } 52 | newBalanceBytes := make([]byte, binary.MaxVarintLen64) 53 | binary.BigEndian.PutUint64(newBalanceBytes, balance - 5) 54 | app.state.Put(transaction.Owner, append([]byte("balance__"), newBalanceBytes...)) 55 | 56 | app.state.Put(append([]byte("name__"), transaction.Name...), transaction.Owner) 57 | } 58 | 59 | func (app *Registrar) Namespace() [namespaceSize]byte { 60 | var empty [namespaceSize]byte 61 | if app.namespace == empty { 62 | var namespace [namespaceSize]byte 63 | copy(namespace[:], []byte("reggie")) 64 | return namespace 65 | } 66 | return app.namespace 67 | } 68 | 69 | func (app *Registrar) SetNamespace(namespace [namespaceSize]byte) { 70 | app.namespace = namespace 71 | } 72 | 73 | func (app *Registrar) SetBlockHead(hash []byte) { 74 | app.state.Put([]byte("__head__"), hash) 75 | } 76 | 77 | func (app *Registrar) BlockHead() []byte { 78 | head, _ := app.state.Get([]byte("__head__")) 79 | return head 80 | } 81 | 82 | func (app *Registrar) Name(name []byte) []byte { 83 | value, err := app.state.Get(append([]byte("name__"), name...)) 84 | if err != nil { 85 | return []byte{} 86 | } 87 | return value 88 | } 89 | 90 | func (app *Registrar) Balance(key []byte) uint64 { 91 | balance, err := app.state.Get(append([]byte("balance__"), key...)) 92 | if err != nil { 93 | return 0 94 | } 95 | return binary.BigEndian.Uint64(balance) 96 | } 97 | 98 | func (app *Registrar) transferCallback(from []byte, to []byte, value int) { 99 | if bytes.Compare(app.owner, to) == 0 { 100 | balanceBytes, err := app.state.Get(append([]byte("balance__"), from...)) 101 | var balance uint64 102 | if err != nil { 103 | balance = 0 104 | } else { 105 | balance = binary.BigEndian.Uint64(balanceBytes) 106 | } 107 | newBalanceBytes := make([]byte, binary.MaxVarintLen64) 108 | binary.BigEndian.PutUint64(newBalanceBytes, balance + uint64(value)) 109 | app.state.Put(append([]byte("balance__"), from...), newBalanceBytes) 110 | } 111 | } 112 | 113 | func (app *Registrar) GenerateTransaction(owner crypto.PrivKey, name []byte) Message { 114 | ownerPubKeyBytes, _ := owner.GetPublic().Bytes() 115 | transactionMessage := &RegisterTransactionMessage{ 116 | Name: name, 117 | } 118 | signedData, _ := proto.Marshal(transactionMessage) 119 | signature, _ := owner.Sign(signedData) 120 | transaction := &RegisterTransaction{ 121 | Owner: ownerPubKeyBytes, 122 | Name: name, 123 | Signature: signature, 124 | } 125 | messageData, _ := proto.Marshal(transaction) 126 | return *NewMessage(app.Namespace(), messageData) 127 | } 128 | -------------------------------------------------------------------------------- /app_registrar.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: app_registrar.proto 3 | 4 | package lazyledger 5 | 6 | import ( 7 | fmt "fmt" 8 | proto "github.com/golang/protobuf/proto" 9 | math "math" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | // This is a compile-time assertion to ensure that this generated file 18 | // is compatible with the proto package it is being compiled against. 19 | // A compilation error at this line likely means your copy of the 20 | // proto package needs to be updated. 21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 22 | 23 | type RegisterTransaction struct { 24 | Owner []byte `protobuf:"bytes,1,req,name=owner" json:"owner,omitempty"` 25 | Name []byte `protobuf:"bytes,2,req,name=name" json:"name,omitempty"` 26 | Signature []byte `protobuf:"bytes,3,req,name=signature" json:"signature,omitempty"` 27 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 28 | XXX_unrecognized []byte `json:"-"` 29 | XXX_sizecache int32 `json:"-"` 30 | } 31 | 32 | func (m *RegisterTransaction) Reset() { *m = RegisterTransaction{} } 33 | func (m *RegisterTransaction) String() string { return proto.CompactTextString(m) } 34 | func (*RegisterTransaction) ProtoMessage() {} 35 | func (*RegisterTransaction) Descriptor() ([]byte, []int) { 36 | return fileDescriptor_84f106271c15fd48, []int{0} 37 | } 38 | 39 | func (m *RegisterTransaction) XXX_Unmarshal(b []byte) error { 40 | return xxx_messageInfo_RegisterTransaction.Unmarshal(m, b) 41 | } 42 | func (m *RegisterTransaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 43 | return xxx_messageInfo_RegisterTransaction.Marshal(b, m, deterministic) 44 | } 45 | func (m *RegisterTransaction) XXX_Merge(src proto.Message) { 46 | xxx_messageInfo_RegisterTransaction.Merge(m, src) 47 | } 48 | func (m *RegisterTransaction) XXX_Size() int { 49 | return xxx_messageInfo_RegisterTransaction.Size(m) 50 | } 51 | func (m *RegisterTransaction) XXX_DiscardUnknown() { 52 | xxx_messageInfo_RegisterTransaction.DiscardUnknown(m) 53 | } 54 | 55 | var xxx_messageInfo_RegisterTransaction proto.InternalMessageInfo 56 | 57 | func (m *RegisterTransaction) GetOwner() []byte { 58 | if m != nil { 59 | return m.Owner 60 | } 61 | return nil 62 | } 63 | 64 | func (m *RegisterTransaction) GetName() []byte { 65 | if m != nil { 66 | return m.Name 67 | } 68 | return nil 69 | } 70 | 71 | func (m *RegisterTransaction) GetSignature() []byte { 72 | if m != nil { 73 | return m.Signature 74 | } 75 | return nil 76 | } 77 | 78 | type RegisterTransactionMessage struct { 79 | Name []byte `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` 80 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 81 | XXX_unrecognized []byte `json:"-"` 82 | XXX_sizecache int32 `json:"-"` 83 | } 84 | 85 | func (m *RegisterTransactionMessage) Reset() { *m = RegisterTransactionMessage{} } 86 | func (m *RegisterTransactionMessage) String() string { return proto.CompactTextString(m) } 87 | func (*RegisterTransactionMessage) ProtoMessage() {} 88 | func (*RegisterTransactionMessage) Descriptor() ([]byte, []int) { 89 | return fileDescriptor_84f106271c15fd48, []int{1} 90 | } 91 | 92 | func (m *RegisterTransactionMessage) XXX_Unmarshal(b []byte) error { 93 | return xxx_messageInfo_RegisterTransactionMessage.Unmarshal(m, b) 94 | } 95 | func (m *RegisterTransactionMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 96 | return xxx_messageInfo_RegisterTransactionMessage.Marshal(b, m, deterministic) 97 | } 98 | func (m *RegisterTransactionMessage) XXX_Merge(src proto.Message) { 99 | xxx_messageInfo_RegisterTransactionMessage.Merge(m, src) 100 | } 101 | func (m *RegisterTransactionMessage) XXX_Size() int { 102 | return xxx_messageInfo_RegisterTransactionMessage.Size(m) 103 | } 104 | func (m *RegisterTransactionMessage) XXX_DiscardUnknown() { 105 | xxx_messageInfo_RegisterTransactionMessage.DiscardUnknown(m) 106 | } 107 | 108 | var xxx_messageInfo_RegisterTransactionMessage proto.InternalMessageInfo 109 | 110 | func (m *RegisterTransactionMessage) GetName() []byte { 111 | if m != nil { 112 | return m.Name 113 | } 114 | return nil 115 | } 116 | 117 | func init() { 118 | proto.RegisterType((*RegisterTransaction)(nil), "lazyledger.RegisterTransaction") 119 | proto.RegisterType((*RegisterTransactionMessage)(nil), "lazyledger.RegisterTransactionMessage") 120 | } 121 | 122 | func init() { proto.RegisterFile("app_registrar.proto", fileDescriptor_84f106271c15fd48) } 123 | 124 | var fileDescriptor_84f106271c15fd48 = []byte{ 125 | // 146 bytes of a gzipped FileDescriptorProto 126 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x2c, 0x28, 0x88, 127 | 0x2f, 0x4a, 0x4d, 0xcf, 0x2c, 0x2e, 0x29, 0x4a, 0x2c, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 128 | 0xe2, 0xca, 0x49, 0xac, 0xaa, 0xcc, 0x49, 0x4d, 0x49, 0x4f, 0x2d, 0x52, 0x8a, 0xe5, 0x12, 0x0e, 129 | 0x02, 0x4b, 0xa7, 0x16, 0x85, 0x14, 0x25, 0xe6, 0x15, 0x27, 0x26, 0x97, 0x64, 0xe6, 0xe7, 0x09, 130 | 0x89, 0x70, 0xb1, 0xe6, 0x97, 0xe7, 0xa5, 0x16, 0x49, 0x30, 0x2a, 0x30, 0x69, 0xf0, 0x04, 0x41, 131 | 0x38, 0x42, 0x42, 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x4c, 0x60, 0x41, 0x30, 0x5b, 0x48, 132 | 0x86, 0x8b, 0xb3, 0x38, 0x33, 0x3d, 0x2f, 0xb1, 0xa4, 0xb4, 0x28, 0x55, 0x82, 0x19, 0x2c, 0x81, 133 | 0x10, 0x50, 0x32, 0xe0, 0x92, 0xc2, 0x62, 0xbc, 0x6f, 0x6a, 0x71, 0x71, 0x62, 0x7a, 0x2a, 0xdc, 134 | 0x3c, 0x46, 0x84, 0x79, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x33, 0xb6, 0x18, 0xac, 0xb2, 0x00, 135 | 0x00, 0x00, 136 | } 137 | -------------------------------------------------------------------------------- /app_registrar.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | package lazyledger; 3 | 4 | message RegisterTransaction { 5 | required bytes owner = 1; 6 | required bytes name = 2; 7 | required bytes signature = 3; 8 | } 9 | 10 | message RegisterTransactionMessage { 11 | required bytes name = 1; 12 | } 13 | -------------------------------------------------------------------------------- /app_registrar_test.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "testing" 7 | "bytes" 8 | 9 | "github.com/libp2p/go-libp2p-crypto" 10 | ) 11 | 12 | func TestAppRegistrarSimpleBlock(t *testing.T) { 13 | bs := NewSimpleBlockStore() 14 | b := NewBlockchain(bs) 15 | 16 | sb := NewSimpleBlock([]byte{0}) 17 | 18 | ms1 := NewSimpleMap() 19 | currencyApp := NewCurrency(ms1, b) 20 | b.RegisterApplication(¤cyApp) 21 | 22 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 23 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 24 | pubABytes, _ := pubA.Bytes() 25 | pubBBytes, _ := pubB.Bytes() 26 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 27 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000) 28 | ms1.Put(pubABytes, pubABalanceBytes) 29 | 30 | ms2 := NewSimpleMap() 31 | registrarApp := NewRegistrar(ms2, currencyApp.(*Currency), pubBBytes) 32 | b.RegisterApplication(®istrarApp) 33 | 34 | sb.AddMessage(currencyApp.(*Currency).GenerateTransaction(privA, pubB, 100, nil)) 35 | sb.AddMessage(registrarApp.(*Registrar).GenerateTransaction(privA, []byte("foo"))) 36 | b.ProcessBlock(sb) 37 | 38 | if currencyApp.(*Currency).Balance(pubA) != 900 || currencyApp.(*Currency).Balance(pubB) != 100 { 39 | t.Error("test tranasaction failed: invalid post-balances") 40 | } 41 | if registrarApp.(*Registrar).Balance(pubABytes) != 100 { 42 | t.Error("test tranasaction failed: invalid post-balances in registrar") 43 | } 44 | if bytes.Compare(registrarApp.(*Registrar).Name([]byte("foo")), pubABytes) != 0 { 45 | t.Error("failed to register name") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /application.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | // Application is an interface for a lazyledger application. 4 | type Application interface { 5 | // ProcessMessage processes a message according to the application's state machine. 6 | ProcessMessage(message Message) 7 | 8 | // Namespace returns the namespace ID of the application. 9 | Namespace() [namespaceSize]byte 10 | 11 | // BlockHead returns the hash of the latest block that has been processed. 12 | BlockHead() []byte 13 | 14 | // SetBlockHead sets the hash of the latest block that has been processed. 15 | SetBlockHead(hash []byte) 16 | } 17 | -------------------------------------------------------------------------------- /block.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | // Block represents a block in the chain. 4 | type Block interface { 5 | // AddMessage adds a message to the block. 6 | AddMessage(Message) 7 | 8 | // Digest computes the hash of the block. 9 | Digest() []byte 10 | 11 | // Valid returns true if the block is valid. 12 | Valid() bool 13 | 14 | // PrevHash returns the hash of the previous block. 15 | PrevHash() []byte 16 | 17 | // Messages returns the block's messages. 18 | Messages() []Message 19 | 20 | // ApplicationProof creates a Merkle proof for all of the messages in a block for an application namespace. 21 | //ApplicationProof([namespaceSize]byte) (int, int, [][]byte, *[]Message, [][]byte) 22 | 23 | // VerifyApplicationProof verifies a Merkle proof for all of the messages in a block for an application namespace. 24 | //VerifyApplicationProof([namespaceSize]byte, int, int, [][]byte, *[]Message, [][]byte) bool 25 | 26 | ProveDependency(int) ([]byte, [][]byte, error) 27 | 28 | VerifyDependency(int, []byte, [][]byte) bool 29 | 30 | DependencyProven([]byte) bool 31 | } 32 | -------------------------------------------------------------------------------- /blockchain.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | // Blockchain is a chain of blocks. 8 | // This is a prototype for testing purposes and thus does not support re-orgs, and there is no network stack. 9 | type Blockchain struct { 10 | blockStore BlockStore 11 | headBlock Block 12 | applications []*Application 13 | } 14 | 15 | // NewBlockchain returns a new blockchain. 16 | func NewBlockchain(blockStore BlockStore) *Blockchain { 17 | return &Blockchain{ 18 | blockStore: blockStore, 19 | } 20 | } 21 | 22 | // ProcessBlock processes a new block. 23 | func (b *Blockchain) ProcessBlock(block Block) { 24 | b.blockStore.Put(block.Digest(), block) 25 | 26 | if b.headBlock == nil || bytes.Compare(block.PrevHash(), b.headBlock.Digest()) == 0 { 27 | b.headBlock = block 28 | b.processCallbacks(block, true) 29 | } else { 30 | b.processCallbacks(block, false) 31 | } 32 | } 33 | 34 | func (b *Blockchain) Block(digest []byte) (Block, error) { 35 | block, err := b.blockStore.Get(digest) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return block, nil 40 | } 41 | 42 | // RegisterApplication registers an application instance to call when new relevant messages arrive. 43 | func (b *Blockchain) RegisterApplication(application *Application) { 44 | b.applications = append(b.applications, application) 45 | } 46 | 47 | func (b *Blockchain) processCallbacks(block Block, isHead bool) { 48 | for _, application := range b.applications { 49 | if isHead { 50 | (*application).SetBlockHead(block.Digest()) 51 | } 52 | for _, message := range block.Messages() { 53 | if message.Namespace() == (*application).Namespace() { 54 | (*application).ProcessMessage(message) 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /blockstore.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | // BlockStore is a key-value store for blocks. 4 | type BlockStore interface { 5 | Get(key []byte) (Block, error) // Get gets the value for a key. 6 | Put(key []byte, value Block) error // Put updates the value for a key. 7 | Del(key []byte) error // Del deletes a key. 8 | } 9 | 10 | // SimpleBlockStore is a simple in-memory block store. 11 | type SimpleBlockStore struct { 12 | m map[string]Block 13 | } 14 | 15 | // NewSimpleBlockStore creates a new empty simple block store. 16 | func NewSimpleBlockStore() *SimpleBlockStore { 17 | return &SimpleBlockStore{ 18 | m: make(map[string]Block), 19 | } 20 | } 21 | 22 | // Get gets the value for a key. 23 | func (sbs *SimpleBlockStore) Get(key []byte) (Block, error) { 24 | if value, ok := sbs.m[string(key)]; ok { 25 | return value, nil 26 | } 27 | return nil, &InvalidKeyError{Key: key} 28 | } 29 | 30 | // Put updates the value for a key. 31 | func (sbs *SimpleBlockStore) Put(key []byte, value Block) error { 32 | sbs.m[string(key)] = value 33 | return nil 34 | } 35 | 36 | // Del deletes a key. 37 | func (sbs *SimpleBlockStore) Del(key []byte) error { 38 | _, ok := sbs.m[string(key)] 39 | if ok { 40 | delete(sbs.m, string(key)) 41 | return nil 42 | } 43 | return &InvalidKeyError{Key: key} 44 | } 45 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_1/.gitignore: -------------------------------------------------------------------------------- 1 | lazyledger_measurements_1 2 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "crypto/rand" 6 | 7 | "github.com/lazyledger/lazyledger-prototype" 8 | ) 9 | 10 | const namespaceSize = 8 11 | 12 | func main() { 13 | txAmounts := []int{128, 256, 384, 512, 640, 768, 896, 1024} 14 | txSize := 128 15 | for _, txes := range txAmounts { 16 | sb := generateSimpleBlock(txes, txSize) 17 | sbBandwidth := len(sb.PrevHash()) + len(sb.MessagesRoot()) + txSize * txes 18 | 19 | pb := generateProbabilisticBlock(txes, txSize) 20 | req, _ := pb.RequestSamples(10) 21 | res := pb.RespondSamples(req) 22 | pbBandwidth := 0 23 | for _, root := range pb.RowRoots() { 24 | pbBandwidth += len(root) 25 | } 26 | for _, root := range pb.ColumnRoots() { 27 | pbBandwidth += len(root) 28 | } 29 | for _, proof := range res.Proofs { 30 | for _, hash := range proof { 31 | pbBandwidth += len(hash) 32 | } 33 | } 34 | 35 | fmt.Println(txes, sbBandwidth, pbBandwidth) 36 | } 37 | } 38 | 39 | func generateSimpleBlock(txes int, txSize int) *lazyledger.SimpleBlock { 40 | txSize -= namespaceSize 41 | sb := lazyledger.NewSimpleBlock([]byte{0}) 42 | 43 | for i := 0; i < txes; i++ { 44 | messageData := make([]byte, txSize) 45 | rand.Read(messageData) 46 | sb.AddMessage(*lazyledger.NewMessage([namespaceSize]byte{0}, messageData)) 47 | } 48 | 49 | return sb.(*lazyledger.SimpleBlock) 50 | } 51 | 52 | func generateProbabilisticBlock(txes int, txSize int) *lazyledger.ProbabilisticBlock { 53 | pb := lazyledger.NewProbabilisticBlock([]byte{0}, txSize) 54 | txSize -= namespaceSize + 2 55 | 56 | for i := 0; i < txes; i++ { 57 | messageData := make([]byte, txSize) 58 | rand.Read(messageData) 59 | pb.AddMessage(*lazyledger.NewMessage([namespaceSize]byte{0}, messageData)) 60 | } 61 | 62 | return pb.(*lazyledger.ProbabilisticBlock) 63 | } 64 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_2/.gitignore: -------------------------------------------------------------------------------- 1 | lazyledger_measurements_2 2 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/lazyledger/lazyledger-prototype" 9 | "github.com/libp2p/go-libp2p-crypto" 10 | ) 11 | 12 | const namespaceSize = 8 13 | 14 | func main() { 15 | currencyTxes := 10 16 | txAmounts := []int{128, 256, 384, 512, 640, 768, 896, 1024} 17 | txSize := 256 18 | for _, txes := range txAmounts { 19 | sbBandwidthMax := 0 20 | pbBandwidthMax := 0 21 | 22 | for i := 0; i < 10; i++ { 23 | sb, ns := generateSimpleBlock(currencyTxes, txes, txSize) 24 | _, _, proofs1, messages1, _ := sb.ApplicationProof(ns) 25 | sbBandwidth := 0 26 | for _, msg := range *messages1 { 27 | sbBandwidth += len(msg.Marshal()) 28 | } 29 | for _, hash := range proofs1 { 30 | sbBandwidth += len(hash) 31 | } 32 | if sbBandwidth > sbBandwidthMax { 33 | sbBandwidthMax = sbBandwidth 34 | } 35 | 36 | pb, ns := generateProbabilisticBlock(currencyTxes, txes, txSize) 37 | _, _, proofs2, messages2, _ := pb.ApplicationProof(ns) 38 | pbBandwidth := 0 39 | for _, msg := range *messages2 { 40 | pbBandwidth += len(msg.Marshal()) 41 | } 42 | for _, proof := range proofs2 { 43 | for _, hash := range proof { 44 | pbBandwidth += len(hash) 45 | } 46 | } 47 | if pbBandwidth > pbBandwidthMax { 48 | pbBandwidthMax = pbBandwidth 49 | } 50 | } 51 | 52 | fmt.Println(txes*txSize, sbBandwidthMax, pbBandwidthMax) 53 | } 54 | } 55 | 56 | func generateSimpleBlock(currencyTxes int, otherTxes, txSize int) (*lazyledger.SimpleBlock, [namespaceSize]byte) { 57 | txSize -= namespaceSize 58 | 59 | bs := lazyledger.NewSimpleBlockStore() 60 | b := lazyledger.NewBlockchain(bs) 61 | sb := lazyledger.NewSimpleBlock([]byte{0}) 62 | ms := lazyledger.NewSimpleMap() 63 | app := lazyledger.NewCurrency(ms, b) 64 | b.RegisterApplication(&app) 65 | 66 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 67 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 68 | pubABytes, _ := pubA.Bytes() 69 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 70 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 71 | ms.Put(pubABytes, pubABalanceBytes) 72 | 73 | for i := 0; i < currencyTxes; i++ { 74 | sb.AddMessage(app.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 1, nil)) 75 | } 76 | 77 | for i := 0; i < otherTxes; i++ { 78 | messageData := make([]byte, txSize) 79 | rand.Read(messageData) 80 | sb.AddMessage(*lazyledger.NewMessage([namespaceSize]byte{0}, messageData)) 81 | } 82 | 83 | return sb.(*lazyledger.SimpleBlock), app.Namespace() 84 | } 85 | 86 | func generateProbabilisticBlock(currencyTxes int, otherTxes, txSize int) (*lazyledger.ProbabilisticBlock, [namespaceSize]byte) { 87 | pb := lazyledger.NewProbabilisticBlock([]byte{0}, txSize) 88 | txSize -= namespaceSize + 2 89 | 90 | bs := lazyledger.NewSimpleBlockStore() 91 | b := lazyledger.NewBlockchain(bs) 92 | ms := lazyledger.NewSimpleMap() 93 | app := lazyledger.NewCurrency(ms, b) 94 | b.RegisterApplication(&app) 95 | 96 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 97 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 98 | pubABytes, _ := pubA.Bytes() 99 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 100 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 101 | ms.Put(pubABytes, pubABalanceBytes) 102 | 103 | for i := 0; i < currencyTxes; i++ { 104 | pb.AddMessage(app.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 1, nil)) 105 | } 106 | 107 | for i := 0; i < otherTxes; i++ { 108 | messageData := make([]byte, txSize) 109 | rand.Read(messageData) 110 | pb.AddMessage(*lazyledger.NewMessage([namespaceSize]byte{0}, messageData)) 111 | } 112 | 113 | return pb.(*lazyledger.ProbabilisticBlock), app.Namespace() 114 | } 115 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_3/.gitignore: -------------------------------------------------------------------------------- 1 | lazyledger_measurements_3 2 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/lazyledger/lazyledger-prototype" 9 | "github.com/libp2p/go-libp2p-crypto" 10 | ) 11 | 12 | const namespaceSize = 8 13 | 14 | func main() { 15 | currencyTxes := 10 16 | txAmounts := []int{128, 256, 384, 512, 640, 768, 896, 1024} 17 | txSize := 1024 18 | for _, txes := range txAmounts { 19 | _, c, d := generateSimpleBlock(currencyTxes, txes, txSize) 20 | sbStoragec := c.StorageSize() 21 | sbStoraged := d.StorageSize() 22 | 23 | //_, c, d = generateProbabilisticBlock(currencyTxes, txes, txSize) 24 | //pbStoragec := c.StorageSize() 25 | //pbStoraged := d.StorageSize() 26 | 27 | fmt.Println(txes, sbStoragec, sbStoraged)//, pbStoragec, pbStoraged) 28 | } 29 | } 30 | 31 | func generateSimpleBlock(currencyTxes int, otherTxes, txSize int) (*lazyledger.SimpleBlock, *lazyledger.Currency, *lazyledger.DummyApp) { 32 | bs := lazyledger.NewSimpleBlockStore() 33 | b := lazyledger.NewBlockchain(bs) 34 | sb := lazyledger.NewSimpleBlock([]byte{0}) 35 | ms := lazyledger.NewSimpleMap() 36 | app := lazyledger.NewCurrency(ms, b) 37 | b.RegisterApplication(&app) 38 | 39 | ms2 := lazyledger.NewSimpleMap() 40 | app2 := lazyledger.NewDummyApp(ms2) 41 | b.RegisterApplication(&app2) 42 | 43 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 44 | pubABytes, _ := pubA.Bytes() 45 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 46 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 47 | ms.Put(pubABytes, pubABalanceBytes) 48 | 49 | for i := 0; i < currencyTxes; i++ { 50 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 51 | sb.AddMessage(app.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 1, nil)) 52 | } 53 | 54 | for i := 0; i < otherTxes; i++ { 55 | k := make([]byte, txSize / 2) 56 | v := make([]byte, txSize / 2) 57 | puts := make(map[string]string) 58 | rand.Read(k) 59 | rand.Read(v) 60 | puts[string(k)] = string(v) 61 | t := app2.(*lazyledger.DummyApp).GenerateTransaction(puts) 62 | sb.AddMessage(t) 63 | } 64 | 65 | b.ProcessBlock(sb) 66 | 67 | return sb.(*lazyledger.SimpleBlock), app.(*lazyledger.Currency), app2.(*lazyledger.DummyApp) 68 | } 69 | 70 | func generateProbabilisticBlock(currencyTxes int, otherTxes, txSize int) (*lazyledger.ProbabilisticBlock, *lazyledger.Currency, *lazyledger.DummyApp) { 71 | pb := lazyledger.NewProbabilisticBlock([]byte{0}, txSize+20) 72 | 73 | bs := lazyledger.NewSimpleBlockStore() 74 | b := lazyledger.NewBlockchain(bs) 75 | ms := lazyledger.NewSimpleMap() 76 | app := lazyledger.NewCurrency(ms, b) 77 | b.RegisterApplication(&app) 78 | 79 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 80 | pubABytes, _ := pubA.Bytes() 81 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 82 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 83 | ms.Put(pubABytes, pubABalanceBytes) 84 | 85 | ms2 := lazyledger.NewSimpleMap() 86 | app2 := lazyledger.NewDummyApp(ms2) 87 | b.RegisterApplication(&app2) 88 | 89 | for i := 0; i < currencyTxes; i++ { 90 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 91 | pb.AddMessage(app.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 1, nil)) 92 | } 93 | 94 | for i := 0; i < otherTxes; i++ { 95 | k := make([]byte, txSize / 2) 96 | v := make([]byte, txSize / 2) 97 | puts := make(map[string]string) 98 | rand.Read(k) 99 | rand.Read(v) 100 | puts[string(k)] = string(v) 101 | t := app2.(*lazyledger.DummyApp).GenerateTransaction(puts) 102 | pb.AddMessage(t) 103 | } 104 | 105 | b.ProcessBlock(pb) 106 | 107 | return pb.(*lazyledger.ProbabilisticBlock), app.(*lazyledger.Currency), app2.(*lazyledger.DummyApp) 108 | } 109 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_4/.gitignore: -------------------------------------------------------------------------------- 1 | lazyledger_measurements_4 2 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_4/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/lazyledger/lazyledger-prototype" 9 | "github.com/libp2p/go-libp2p-crypto" 10 | ) 11 | 12 | const namespaceSize = 8 13 | 14 | func main() { 15 | registrarTxes := 10 16 | txAmounts := []int{128, 256, 384, 512, 640, 768, 896, 1024} 17 | txSize := 256 18 | for _, txes := range txAmounts { 19 | sbBandwidthMax := 0 20 | pbBandwidthMax := 0 21 | 22 | for i := 0; i < 10; i++ { 23 | sb, cns, rns := generateSimpleBlock(registrarTxes, txes, txSize) 24 | sbBandwidth := 0 25 | _, _, proofs1, messages1, _ := sb.ApplicationProof(cns) 26 | for _, msg := range *messages1 { 27 | sbBandwidth += len(msg.Marshal()) 28 | } 29 | for _, hash := range proofs1 { 30 | sbBandwidth += len(hash) 31 | } 32 | if sbBandwidth > sbBandwidthMax { 33 | sbBandwidthMax = sbBandwidth 34 | } 35 | _, _, proofs1, _, hashes1 := sb.ApplicationProof(rns) 36 | for _, hash := range hashes1 { 37 | sbBandwidth += len(hash) 38 | } 39 | for _, hash := range proofs1 { 40 | sbBandwidth += len(hash) 41 | } 42 | if sbBandwidth > sbBandwidthMax { 43 | sbBandwidthMax = sbBandwidth 44 | } 45 | 46 | pb, cns, rns := generateProbabilisticBlock(registrarTxes, txes, txSize) 47 | _, _, proofs2, messages2, _ := pb.ApplicationProof(cns) 48 | pbBandwidth := 0 49 | for _, msg := range *messages2 { 50 | pbBandwidth += len(msg.Marshal()) 51 | } 52 | for _, proof := range proofs2 { 53 | for _, hash := range proof { 54 | pbBandwidth += len(hash) 55 | } 56 | } 57 | _, _, proofs2, _, hashes2 := pb.ApplicationProof(rns) 58 | for _, hash := range hashes2 { 59 | pbBandwidth += len(hash) 60 | } 61 | for _, proof := range proofs2 { 62 | for _, hash := range proof { 63 | pbBandwidth += len(hash) 64 | } 65 | } 66 | if pbBandwidth > pbBandwidthMax { 67 | pbBandwidthMax = pbBandwidth 68 | } 69 | } 70 | 71 | fmt.Println(txes, sbBandwidthMax, pbBandwidthMax) 72 | } 73 | } 74 | 75 | func generateSimpleBlock(registrarTxes int, otherTxes, txSize int) (*lazyledger.SimpleBlock, [namespaceSize]byte, [namespaceSize]byte) { 76 | txSize -= namespaceSize 77 | 78 | bs := lazyledger.NewSimpleBlockStore() 79 | b := lazyledger.NewBlockchain(bs) 80 | 81 | sb := lazyledger.NewSimpleBlock([]byte{0}) 82 | 83 | ms1 := lazyledger.NewSimpleMap() 84 | currencyApp := lazyledger.NewCurrency(ms1, b) 85 | b.RegisterApplication(¤cyApp) 86 | 87 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 88 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 89 | pubABytes, _ := pubA.Bytes() 90 | pubBBytes, _ := pubB.Bytes() 91 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 92 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 93 | ms1.Put(pubABytes, pubABalanceBytes) 94 | 95 | ms2 := lazyledger.NewSimpleMap() 96 | registrarApp := lazyledger.NewRegistrar(ms2, currencyApp.(*lazyledger.Currency), pubBBytes) 97 | b.RegisterApplication(®istrarApp) 98 | 99 | for i := 0; i < registrarTxes; i++ { 100 | sb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 1, nil)) 101 | } 102 | 103 | ms3 := lazyledger.NewSimpleMap() 104 | registrarApp2 := lazyledger.NewRegistrar(ms3, currencyApp.(*lazyledger.Currency), pubBBytes) 105 | b.RegisterApplication(®istrarApp2) 106 | 107 | for i := 0; i < otherTxes; i++ { 108 | sb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 1, nil)) 109 | } 110 | 111 | return sb.(*lazyledger.SimpleBlock), currencyApp.Namespace(), registrarApp.Namespace() 112 | } 113 | 114 | func generateProbabilisticBlock(registrarTxes int, otherTxes, txSize int) (*lazyledger.ProbabilisticBlock, [namespaceSize]byte, [namespaceSize]byte) { 115 | pb := lazyledger.NewProbabilisticBlock([]byte{0}, txSize) 116 | txSize -= namespaceSize + 2 117 | 118 | bs := lazyledger.NewSimpleBlockStore() 119 | b := lazyledger.NewBlockchain(bs) 120 | 121 | ms1 := lazyledger.NewSimpleMap() 122 | currencyApp := lazyledger.NewCurrency(ms1, b) 123 | b.RegisterApplication(¤cyApp) 124 | 125 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 126 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 127 | pubABytes, _ := pubA.Bytes() 128 | pubBBytes, _ := pubB.Bytes() 129 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 130 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 131 | ms1.Put(pubABytes, pubABalanceBytes) 132 | 133 | ms2 := lazyledger.NewSimpleMap() 134 | registrarApp := lazyledger.NewRegistrar(ms2, currencyApp.(*lazyledger.Currency), pubBBytes) 135 | b.RegisterApplication(®istrarApp) 136 | 137 | for i := 0; i < registrarTxes; i++ { 138 | pb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 1, nil)) 139 | } 140 | 141 | ms3 := lazyledger.NewSimpleMap() 142 | registrarApp2 := lazyledger.NewRegistrar(ms3, currencyApp.(*lazyledger.Currency), pubBBytes) 143 | b.RegisterApplication(®istrarApp2) 144 | 145 | for i := 0; i < otherTxes; i++ { 146 | pb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 1, nil)) 147 | } 148 | 149 | return pb.(*lazyledger.ProbabilisticBlock), currencyApp.Namespace(), registrarApp.Namespace() 150 | } 151 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_5/.gitignore: -------------------------------------------------------------------------------- 1 | lazyledger_measurements_5 2 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_5/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/lazyledger/lazyledger-prototype" 9 | "github.com/libp2p/go-libp2p-crypto" 10 | ) 11 | 12 | const namespaceSize = 8 13 | 14 | func main() { 15 | registrarTxes := 10 16 | txAmounts := []int{128, 256, 384, 512, 640, 768, 896, 1024} 17 | txSize := 256 18 | for _, txes := range txAmounts { 19 | sbBandwidthMax := 0 20 | pbBandwidthMax := 0 21 | 22 | for i := 0; i < 10; i++ { 23 | sb, cns, rns := generateSimpleBlock(registrarTxes, txes, txSize) 24 | sbBandwidth := 0 25 | _, _, proofs1, messages1, _ := sb.ApplicationProof(cns) 26 | for _, msg := range *messages1 { 27 | sbBandwidth += len(msg.Marshal()) 28 | } 29 | for _, hash := range proofs1 { 30 | sbBandwidth += len(hash) 31 | } 32 | if sbBandwidth > sbBandwidthMax { 33 | sbBandwidthMax = sbBandwidth 34 | } 35 | _, _, proofs1, messages1, _ = sb.ApplicationProof(rns) 36 | for _, msg := range *messages1 { 37 | sbBandwidth += len(msg.Marshal()) 38 | } 39 | for _, hash := range proofs1 { 40 | sbBandwidth += len(hash) 41 | } 42 | if sbBandwidth > sbBandwidthMax { 43 | sbBandwidthMax = sbBandwidth 44 | } 45 | 46 | pb, cns, rns := generateProbabilisticBlock(registrarTxes, txes, txSize) 47 | _, _, proofs2, messages2, _ := pb.ApplicationProof(cns) 48 | pbBandwidth := 0 49 | for _, msg := range *messages2 { 50 | pbBandwidth += len(msg.Marshal()) 51 | } 52 | for _, proof := range proofs2 { 53 | for _, hash := range proof { 54 | pbBandwidth += len(hash) 55 | } 56 | } 57 | _, _, proofs2, messages2, _ = pb.ApplicationProof(rns) 58 | for _, msg := range *messages2 { 59 | pbBandwidth += len(msg.Marshal()) 60 | } 61 | for _, proof := range proofs2 { 62 | for _, hash := range proof { 63 | pbBandwidth += len(hash) 64 | } 65 | } 66 | if pbBandwidth > pbBandwidthMax { 67 | pbBandwidthMax = pbBandwidth 68 | } 69 | } 70 | 71 | fmt.Println(txes, sbBandwidthMax, pbBandwidthMax) 72 | } 73 | } 74 | 75 | func generateSimpleBlock(registrarTxes int, otherTxes, txSize int) (*lazyledger.SimpleBlock, [namespaceSize]byte, [namespaceSize]byte) { 76 | txSize -= namespaceSize 77 | 78 | bs := lazyledger.NewSimpleBlockStore() 79 | b := lazyledger.NewBlockchain(bs) 80 | 81 | sb := lazyledger.NewSimpleBlock([]byte{0}) 82 | 83 | ms1 := lazyledger.NewSimpleMap() 84 | currencyApp := lazyledger.NewCurrency(ms1, b) 85 | b.RegisterApplication(¤cyApp) 86 | 87 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 88 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 89 | _, pubC, _ := crypto.GenerateSecp256k1Key(rand.Reader) 90 | pubABytes, _ := pubA.Bytes() 91 | pubBBytes, _ := pubB.Bytes() 92 | pubCBytes, _ := pubC.Bytes() 93 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 94 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 95 | ms1.Put(pubABytes, pubABalanceBytes) 96 | 97 | ms2 := lazyledger.NewSimpleMap() 98 | registrarApp := lazyledger.NewRegistrar(ms2, currencyApp.(*lazyledger.Currency), pubBBytes) 99 | var rns1 [namespaceSize]byte 100 | copy(rns1[:], []byte("reg1")) 101 | registrarApp.(*lazyledger.Registrar).SetNamespace(rns1) 102 | b.RegisterApplication(®istrarApp) 103 | sb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 100000, nil)) 104 | 105 | for i := 0; i < registrarTxes; i++ { 106 | name := make([]byte, 8) 107 | rand.Read(name) 108 | sb.AddMessage(registrarApp.(*lazyledger.Registrar).GenerateTransaction(privA, name)) 109 | } 110 | 111 | ms3 := lazyledger.NewSimpleMap() 112 | registrarApp2 := lazyledger.NewRegistrar(ms3, currencyApp.(*lazyledger.Currency), pubCBytes) 113 | var rns2 [namespaceSize]byte 114 | copy(rns2[:], []byte("reg2")) 115 | registrarApp2.(*lazyledger.Registrar).SetNamespace(rns2) 116 | b.RegisterApplication(®istrarApp2) 117 | sb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubC, 100000, nil)) 118 | 119 | for i := 0; i < otherTxes; i++ { 120 | name := make([]byte, 8) 121 | rand.Read(name) 122 | sb.AddMessage(registrarApp2.(*lazyledger.Registrar).GenerateTransaction(privA, name)) 123 | } 124 | 125 | return sb.(*lazyledger.SimpleBlock), currencyApp.Namespace(), registrarApp.Namespace() 126 | } 127 | 128 | func generateProbabilisticBlock(registrarTxes int, otherTxes, txSize int) (*lazyledger.ProbabilisticBlock, [namespaceSize]byte, [namespaceSize]byte) { 129 | pb := lazyledger.NewProbabilisticBlock([]byte{0}, txSize) 130 | txSize -= namespaceSize + 2 131 | 132 | bs := lazyledger.NewSimpleBlockStore() 133 | b := lazyledger.NewBlockchain(bs) 134 | 135 | ms1 := lazyledger.NewSimpleMap() 136 | currencyApp := lazyledger.NewCurrency(ms1, b) 137 | b.RegisterApplication(¤cyApp) 138 | 139 | privA, pubA, _ := crypto.GenerateSecp256k1Key(rand.Reader) 140 | _, pubB, _ := crypto.GenerateSecp256k1Key(rand.Reader) 141 | _, pubC, _ := crypto.GenerateSecp256k1Key(rand.Reader) 142 | pubABytes, _ := pubA.Bytes() 143 | pubBBytes, _ := pubB.Bytes() 144 | pubCBytes, _ := pubC.Bytes() 145 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 146 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 147 | ms1.Put(pubABytes, pubABalanceBytes) 148 | 149 | ms2 := lazyledger.NewSimpleMap() 150 | registrarApp := lazyledger.NewRegistrar(ms2, currencyApp.(*lazyledger.Currency), pubBBytes) 151 | var rns1 [namespaceSize]byte 152 | copy(rns1[:], []byte("reg1")) 153 | registrarApp.(*lazyledger.Registrar).SetNamespace(rns1) 154 | b.RegisterApplication(®istrarApp) 155 | pb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 100000, nil)) 156 | 157 | for i := 0; i < registrarTxes; i++ { 158 | name := make([]byte, 8) 159 | rand.Read(name) 160 | pb.AddMessage(registrarApp.(*lazyledger.Registrar).GenerateTransaction(privA, name)) 161 | } 162 | 163 | ms3 := lazyledger.NewSimpleMap() 164 | registrarApp2 := lazyledger.NewRegistrar(ms3, currencyApp.(*lazyledger.Currency), pubCBytes) 165 | var rns2 [namespaceSize]byte 166 | copy(rns2[:], []byte("reg2")) 167 | registrarApp2.(*lazyledger.Registrar).SetNamespace(rns2) 168 | b.RegisterApplication(®istrarApp2) 169 | pb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubC, 100000, nil)) 170 | 171 | for i := 0; i < otherTxes; i++ { 172 | name := make([]byte, 8) 173 | rand.Read(name) 174 | pb.AddMessage(registrarApp2.(*lazyledger.Registrar).GenerateTransaction(privA, name)) 175 | } 176 | 177 | return pb.(*lazyledger.ProbabilisticBlock), currencyApp.Namespace(), registrarApp.Namespace() 178 | } 179 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_5d/.gitignore: -------------------------------------------------------------------------------- 1 | lazyledger_measurements_5d 2 | -------------------------------------------------------------------------------- /cmd/lazyledger_measurements_5d/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/lazyledger/lazyledger-prototype" 9 | "github.com/libp2p/go-libp2p-crypto" 10 | ) 11 | 12 | var privA crypto.PrivKey 13 | var pubA crypto.PubKey 14 | var pubB crypto.PubKey 15 | var pubC crypto.PubKey 16 | 17 | const namespaceSize = 8 18 | 19 | func main() { 20 | privA, pubA, _ = crypto.GenerateSecp256k1Key(rand.Reader) 21 | _, pubB, _ = crypto.GenerateSecp256k1Key(rand.Reader) 22 | _, pubC, _ = crypto.GenerateSecp256k1Key(rand.Reader) 23 | 24 | registrarTxes := 10 25 | txAmounts := []int{128, 256, 384, 512, 640, 768, 896, 1024} 26 | txSize := 256 27 | for _, txes := range txAmounts { 28 | sbBandwidthMax := 0 29 | pbBandwidthMax := 0 30 | 31 | for i := 0; i < 10; i++ { 32 | sb, cns, rns := generateSimpleBlock(registrarTxes, txes, txSize) 33 | sbBandwidth := 0 34 | _, _, proofs1, messages1, _ := sb.ApplicationProof(cns) 35 | for _, msg := range *messages1 { 36 | sbBandwidth += len(msg.Marshal()) 37 | } 38 | for _, hash := range proofs1 { 39 | sbBandwidth += len(hash) 40 | } 41 | if sbBandwidth > sbBandwidthMax { 42 | sbBandwidthMax = sbBandwidth 43 | } 44 | _, _, proofs1, messages1, _ = sb.ApplicationProof(rns) 45 | for _, msg := range *messages1 { 46 | sbBandwidth += len(msg.Marshal()) 47 | } 48 | for _, hash := range proofs1 { 49 | sbBandwidth += len(hash) 50 | } 51 | if sbBandwidth > sbBandwidthMax { 52 | sbBandwidthMax = sbBandwidth 53 | } 54 | 55 | pb, cns, rns := generateProbabilisticBlock(registrarTxes, txes, txSize) 56 | _, _, proofs2, messages2, _ := pb.ApplicationProof(cns) 57 | pbBandwidth := 0 58 | for _, msg := range *messages2 { 59 | pbBandwidth += len(msg.Marshal()) 60 | } 61 | for _, proof := range proofs2 { 62 | for _, hash := range proof { 63 | pbBandwidth += len(hash) 64 | } 65 | } 66 | _, _, proofs2, messages2, _ = pb.ApplicationProof(rns) 67 | for _, msg := range *messages2 { 68 | pbBandwidth += len(msg.Marshal()) 69 | } 70 | for _, proof := range proofs2 { 71 | for _, hash := range proof { 72 | pbBandwidth += len(hash) 73 | } 74 | } 75 | if pbBandwidth > pbBandwidthMax { 76 | pbBandwidthMax = pbBandwidth 77 | } 78 | } 79 | 80 | fmt.Println(txes, sbBandwidthMax, pbBandwidthMax) 81 | } 82 | } 83 | 84 | func generateSimpleBlock(registrarTxes int, otherTxes, txSize int) (*lazyledger.SimpleBlock, [namespaceSize]byte, [namespaceSize]byte) { 85 | txSize -= namespaceSize 86 | 87 | bs := lazyledger.NewSimpleBlockStore() 88 | b := lazyledger.NewBlockchain(bs) 89 | 90 | sb := lazyledger.NewSimpleBlock([]byte{0}) 91 | 92 | ms1 := lazyledger.NewSimpleMap() 93 | currencyApp := lazyledger.NewCurrency(ms1, b) 94 | b.RegisterApplication(¤cyApp) 95 | 96 | pubABytes, _ := pubA.Bytes() 97 | pubBBytes, _ := pubB.Bytes() 98 | pubCBytes, _ := pubC.Bytes() 99 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 100 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 101 | ms1.Put(pubABytes, pubABalanceBytes) 102 | 103 | ms2 := lazyledger.NewSimpleMap() 104 | registrarApp := lazyledger.NewRegistrar(ms2, currencyApp.(*lazyledger.Currency), pubBBytes) 105 | var rns1 [namespaceSize]byte 106 | copy(rns1[:], []byte("reg1")) 107 | registrarApp.(*lazyledger.Registrar).SetNamespace(rns1) 108 | b.RegisterApplication(®istrarApp) 109 | sb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 100000, nil)) 110 | 111 | for i := 0; i < registrarTxes; i++ { 112 | name := make([]byte, 8) 113 | //rand.Read(name) 114 | sb.AddMessage(registrarApp.(*lazyledger.Registrar).GenerateTransaction(privA, name)) 115 | } 116 | 117 | ms3 := lazyledger.NewSimpleMap() 118 | registrarApp2 := lazyledger.NewRegistrar(ms3, currencyApp.(*lazyledger.Currency), pubCBytes) 119 | var rns2 [namespaceSize]byte 120 | copy(rns2[:], []byte("reg2")) 121 | registrarApp2.(*lazyledger.Registrar).SetNamespace(rns2) 122 | b.RegisterApplication(®istrarApp2) 123 | sb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubC, 100000, nil)) 124 | 125 | for i := 0; i < otherTxes; i++ { 126 | name := make([]byte, 8) 127 | //rand.Read(name) 128 | sb.AddMessage(registrarApp2.(*lazyledger.Registrar).GenerateTransaction(privA, name)) 129 | } 130 | 131 | return sb.(*lazyledger.SimpleBlock), currencyApp.Namespace(), registrarApp.Namespace() 132 | } 133 | 134 | func generateProbabilisticBlock(registrarTxes int, otherTxes, txSize int) (*lazyledger.ProbabilisticBlock, [namespaceSize]byte, [namespaceSize]byte) { 135 | pb := lazyledger.NewProbabilisticBlock([]byte{0}, txSize) 136 | txSize -= namespaceSize + 2 137 | 138 | bs := lazyledger.NewSimpleBlockStore() 139 | b := lazyledger.NewBlockchain(bs) 140 | 141 | ms1 := lazyledger.NewSimpleMap() 142 | currencyApp := lazyledger.NewCurrency(ms1, b) 143 | b.RegisterApplication(¤cyApp) 144 | 145 | pubABytes, _ := pubA.Bytes() 146 | pubBBytes, _ := pubB.Bytes() 147 | pubCBytes, _ := pubC.Bytes() 148 | pubABalanceBytes := make([]byte, binary.MaxVarintLen64) 149 | binary.BigEndian.PutUint64(pubABalanceBytes, 1000000) 150 | ms1.Put(pubABytes, pubABalanceBytes) 151 | 152 | ms2 := lazyledger.NewSimpleMap() 153 | registrarApp := lazyledger.NewRegistrar(ms2, currencyApp.(*lazyledger.Currency), pubBBytes) 154 | var rns1 [namespaceSize]byte 155 | copy(rns1[:], []byte("reg1")) 156 | registrarApp.(*lazyledger.Registrar).SetNamespace(rns1) 157 | b.RegisterApplication(®istrarApp) 158 | pb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubB, 100000, nil)) 159 | 160 | for i := 0; i < registrarTxes; i++ { 161 | name := make([]byte, 8) 162 | //rand.Read(name) 163 | pb.AddMessage(registrarApp.(*lazyledger.Registrar).GenerateTransaction(privA, name)) 164 | } 165 | 166 | ms3 := lazyledger.NewSimpleMap() 167 | registrarApp2 := lazyledger.NewRegistrar(ms3, currencyApp.(*lazyledger.Currency), pubCBytes) 168 | var rns2 [namespaceSize]byte 169 | copy(rns2[:], []byte("reg2")) 170 | registrarApp2.(*lazyledger.Registrar).SetNamespace(rns2) 171 | b.RegisterApplication(®istrarApp2) 172 | pb.AddMessage(currencyApp.(*lazyledger.Currency).GenerateTransaction(privA, pubC, 100000, nil)) 173 | 174 | for i := 0; i < otherTxes; i++ { 175 | name := make([]byte, 8) 176 | //rand.Read(name) 177 | pb.AddMessage(registrarApp2.(*lazyledger.Registrar).GenerateTransaction(privA, name)) 178 | } 179 | 180 | return pb.(*lazyledger.ProbabilisticBlock), currencyApp.Namespace(), registrarApp.Namespace() 181 | } 182 | -------------------------------------------------------------------------------- /constants.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | const namespaceSize = 8 4 | const flagSize = 16 5 | 6 | var codedNamespace [namespaceSize]byte 7 | var codedFlag [flagSize]byte 8 | 9 | func init() { 10 | for i, _ := range codedNamespace { 11 | codedNamespace[i] = 0xFF 12 | } 13 | for i, _ := range codedFlag { 14 | codedFlag[i] = 0xFF 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /flagger.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | // Flagger is an interface for computing the flags of bytes of data. 4 | type Flagger interface { 5 | // LeafFlag returns the flag of a raw unhashed leaf. 6 | LeafFlag([]byte) []byte 7 | 8 | // NodeFlag returns the flag of an intermediate node. 9 | NodeFlag([]byte) []byte 10 | 11 | // Union returns the union of two flags. 12 | Union([]byte, []byte) []byte 13 | 14 | // FlagSize returns the number of bytes that LeafFlag or Union will return. 15 | FlagSize() int 16 | } 17 | -------------------------------------------------------------------------------- /flaghasher.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "hash" 5 | ) 6 | 7 | type flagDigest struct { 8 | flagger Flagger 9 | baseHasher hash.Hash 10 | data []byte 11 | codedMode bool 12 | } 13 | 14 | // NewFlagHasher returns a new hash.Hash computing checksums using the bashHasher with flags from flagger. 15 | func NewFlagHasher(flagger Flagger, baseHasher hash.Hash) hash.Hash { 16 | return &flagDigest{ 17 | flagger: flagger, 18 | baseHasher: baseHasher, 19 | } 20 | } 21 | 22 | func (d *flagDigest) setCodedMode(mode bool) { 23 | d.codedMode = mode 24 | } 25 | 26 | func (d *flagDigest) Write(p []byte) (int, error) { 27 | d.data = append(d.data, p...) 28 | return d.baseHasher.Write(p) 29 | } 30 | 31 | func (d *flagDigest) Sum(in []byte) []byte { 32 | in = append(in, d.parentFlag()...) 33 | return d.baseHasher.Sum(in) 34 | } 35 | 36 | func (d *flagDigest) Size() int { 37 | return d.flagger.FlagSize() + d.baseHasher.Size() 38 | } 39 | 40 | func (d *flagDigest) BlockSize() int { 41 | return d.baseHasher.BlockSize() 42 | } 43 | 44 | func (d *flagDigest) Reset() { 45 | d.data = nil 46 | d.baseHasher.Reset() 47 | } 48 | 49 | func (d *flagDigest) leftFlag() []byte { 50 | return d.flagger.NodeFlag(d.data[1:d.Size()+1]) 51 | } 52 | 53 | func (d *flagDigest) rightFlag() []byte { 54 | return d.flagger.NodeFlag(d.data[1+d.Size():]) 55 | } 56 | 57 | func (d *flagDigest) parentFlag() []byte { 58 | if d.isLeaf() { 59 | if d.codedMode { 60 | return codedFlag[:] 61 | } 62 | return d.flagger.LeafFlag(d.mainData()) 63 | } 64 | return d.flagger.Union(d.leftFlag(), d.rightFlag()) 65 | } 66 | 67 | func (d *flagDigest) mainData() []byte { 68 | return d.data[1:] 69 | } 70 | 71 | func (d *flagDigest) isLeaf() bool { 72 | if d.data[0] == byte(0) { 73 | return true 74 | } 75 | return false 76 | } 77 | -------------------------------------------------------------------------------- /flaghasher_test.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | "crypto/rand" 7 | "testing" 8 | ) 9 | 10 | func TestFlagHasher(t *testing.T) { 11 | ndf := NewNamespaceDummyFlagger() 12 | fh := NewFlagHasher(ndf, sha256.New()) 13 | data := make([]byte, 100) 14 | rand.Read(data) 15 | 16 | fh.Write([]byte{0}) 17 | fh.Write(data) 18 | leaf1 := fh.Sum(nil) 19 | fh.Reset() 20 | if (bytes.Compare(ndf.NodeFlag(leaf1), ndf.LeafFlag(data)) != 0) { 21 | t.Error("flag for leaf node incorrect") 22 | } 23 | 24 | fh.Write([]byte{0}) 25 | fh.Write(data) 26 | leaf2 := fh.Sum(nil) 27 | fh.Reset() 28 | if (bytes.Compare(ndf.NodeFlag(leaf2), ndf.LeafFlag(data)) != 0) { 29 | t.Error("flag for leaf node incorrect") 30 | } 31 | 32 | fh.Write([]byte{1}) 33 | fh.Write(leaf1) 34 | fh.Write(leaf2) 35 | parent := fh.Sum(nil) 36 | fh.Reset() 37 | if (bytes.Compare(ndf.NodeFlag(parent), ndf.Union(leaf1, leaf2)) != 0) { 38 | t.Error("flag for parent node incorrect") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /mapstore.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import( 4 | "fmt" 5 | ) 6 | 7 | // MapStore is a key-value store. 8 | type MapStore interface { 9 | Get(key []byte) ([]byte, error) // Get gets the value for a key. 10 | Put(key []byte, value []byte) error // Put updates the value for a key. 11 | Del(key []byte) error // Del deletes a key. 12 | storageSize() int 13 | } 14 | 15 | // InvalidKeyError is thrown when a key that does not exist is being accessed. 16 | type InvalidKeyError struct { 17 | Key []byte 18 | } 19 | 20 | func (e *InvalidKeyError) Error() string { 21 | return fmt.Sprintf("invalid key: %s", e.Key) 22 | } 23 | 24 | // SimpleMap is a simple in-memory map. 25 | type SimpleMap struct { 26 | m map[string][]byte 27 | } 28 | 29 | // NewSimpleMap creates a new empty SimpleMap. 30 | func NewSimpleMap() *SimpleMap { 31 | return &SimpleMap{ 32 | m: make(map[string][]byte), 33 | } 34 | } 35 | 36 | // Get gets the value for a key. 37 | func (sm *SimpleMap) Get(key []byte) ([]byte, error) { 38 | if value, ok := sm.m[string(key)]; ok { 39 | return value, nil 40 | } 41 | return nil, &InvalidKeyError{Key: key} 42 | } 43 | 44 | // Put updates the value for a key. 45 | func (sm *SimpleMap) Put(key []byte, value []byte) error { 46 | sm.m[string(key)] = value 47 | return nil 48 | } 49 | 50 | // Del deletes a key. 51 | func (sm *SimpleMap) Del(key []byte) error { 52 | _, ok := sm.m[string(key)] 53 | if ok { 54 | delete(sm.m, string(key)) 55 | return nil 56 | } 57 | return &InvalidKeyError{Key: key} 58 | } 59 | 60 | func (sm *SimpleMap) storageSize() int { 61 | s := 0 62 | for k, v := range sm.m { 63 | s += len(k) 64 | s += len(v) 65 | } 66 | 67 | return s 68 | } 69 | -------------------------------------------------------------------------------- /merkletree_exports.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "hash" 5 | "math/bits" 6 | ) 7 | 8 | // sum returns the hash of the input data using the specified algorithm. 9 | func sum(h hash.Hash, data ...[]byte) []byte { 10 | h.Reset() 11 | for _, d := range data { 12 | // the Hash interface specifies that Write never returns an error 13 | _, _ = h.Write(d) 14 | } 15 | return h.Sum(nil) 16 | } 17 | 18 | // leafSum returns the hash created from data inserted to form a leaf. Leaf 19 | // sums are calculated using: 20 | // Hash(0x00 || data) 21 | func leafSum(h hash.Hash, data []byte) []byte { 22 | return sum(h, []byte{0x00}, data) 23 | } 24 | 25 | // nextSubtreeSize returns the size of the subtree adjacent to start that does 26 | // not overlap end. 27 | func nextSubtreeSize(start, end uint64) int { 28 | ideal := bits.TrailingZeros64(start) 29 | max := bits.Len64(end-start) - 1 30 | if ideal > max { 31 | return 1 << uint(max) 32 | } 33 | return 1 << uint(ideal) 34 | } 35 | -------------------------------------------------------------------------------- /message.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | // Message represents a namespaced message. 8 | type Message struct { 9 | namespace [namespaceSize]byte 10 | data []byte 11 | } 12 | 13 | // NewMessage returns a new message from its namespace and data. 14 | func NewMessage(namespace [namespaceSize]byte, data []byte) *Message { 15 | return &Message{ 16 | namespace: namespace, 17 | data: data, 18 | } 19 | } 20 | 21 | // UnmarshalMessage returns a message from its marshalled raw data. 22 | func UnmarshalMessage(marshalled []byte) *Message { 23 | var namespace [namespaceSize]byte 24 | copy(namespace[:], marshalled[:namespaceSize]) 25 | return NewMessage(namespace, marshalled[namespaceSize:]) 26 | } 27 | 28 | // UnmarshalPaddedMessage returns a message from its marshalled padded raw data. 29 | func UnmarshalPaddedMessage(marshalled []byte) *Message { 30 | marshalledSizeBytes := make([]byte, 2) 31 | marshalledSizeBytes[0] = marshalled[len(marshalled) - 2] 32 | marshalledSizeBytes[1] = marshalled[len(marshalled) - 1] 33 | marshalled = marshalled[:int(binary.LittleEndian.Uint16(marshalledSizeBytes))] 34 | 35 | var namespace [namespaceSize]byte 36 | copy(namespace[:], marshalled[:namespaceSize]) 37 | return NewMessage(namespace, marshalled[namespaceSize:]) 38 | } 39 | 40 | // Marshal converts a message to raw data. 41 | func (m *Message) Marshal() []byte { 42 | return append(m.namespace[:], m.data...) 43 | } 44 | 45 | // Marshal converts a message to padded raw data. 46 | func (m *Message) MarshalPadded(messageSize int) []byte { 47 | marshalled := append(m.namespace[:], m.data...) 48 | marshalledSizeBytes := make([]byte, 2) 49 | binary.LittleEndian.PutUint16(marshalledSizeBytes, uint16(len(marshalled))) 50 | 51 | padding := make([]byte, messageSize - len(marshalled)) 52 | for i, _ := range padding { 53 | padding[i] = 0x00 54 | } 55 | marshalled = append(marshalled, padding...) 56 | marshalled[len(marshalled) - 2] = marshalledSizeBytes[0] 57 | marshalled[len(marshalled) - 1] = marshalledSizeBytes[1] 58 | return marshalled 59 | } 60 | 61 | // Namespace returns the namespace of a message. 62 | func (m *Message) Namespace() [namespaceSize]byte { 63 | return m.namespace; 64 | } 65 | 66 | // Data returns the data of a message. 67 | func (m *Message) Data() []byte { 68 | return m.data; 69 | } 70 | -------------------------------------------------------------------------------- /namespacedummyflagger.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type namespaceDummyFlagger struct {} 8 | 9 | // NewNamespaceDummyFlagger returns a new dummy flagger for namespaced Merkle trees. 10 | func NewNamespaceDummyFlagger() Flagger { 11 | return &namespaceDummyFlagger{} 12 | } 13 | 14 | func (namespaceDummyFlagger) LeafFlag(leaf []byte) []byte { 15 | return append(leaf[:namespaceSize], leaf[:namespaceSize]...) 16 | } 17 | 18 | func (namespaceDummyFlagger) NodeFlag(node []byte) []byte { 19 | return node[:flagSize] 20 | } 21 | 22 | func (namespaceDummyFlagger) Union(leftFlag []byte, rightFlag []byte) []byte { 23 | namespaces := make([][]byte, 4) 24 | namespaces[0], namespaces[1] = dummyNamespacesFromFlag(leftFlag) 25 | namespaces[2], namespaces[3] = dummyNamespacesFromFlag(rightFlag) 26 | 27 | minNamespace := namespaces[0] 28 | maxNamespace := namespaces[0] 29 | for _, namespace := range namespaces[1:] { 30 | if bytes.Compare(namespace, codedNamespace[:]) == 0 { 31 | continue 32 | } 33 | if bytes.Compare(minNamespace, namespace) > 0 { 34 | minNamespace = namespace 35 | } 36 | if bytes.Compare(maxNamespace, namespace) < 0 { 37 | maxNamespace = namespace 38 | } 39 | } 40 | 41 | return dummyFlagFromNamespaces(minNamespace, maxNamespace) 42 | } 43 | 44 | func (namespaceDummyFlagger) FlagSize() int { 45 | return flagSize 46 | } 47 | 48 | func dummyNamespacesFromFlag(flag []byte) ([]byte, []byte) { 49 | return flag[:namespaceSize], flag[namespaceSize:flagSize] 50 | } 51 | 52 | func dummyFlagFromNamespaces(leftNamespace []byte, rightNamespace []byte) []byte { 53 | return append(leftNamespace, rightNamespace...) 54 | } 55 | -------------------------------------------------------------------------------- /probabilisticblock.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | "crypto/rand" 7 | "math" 8 | "math/big" 9 | 10 | "gitlab.com/NebulousLabs/merkletree" 11 | "github.com/musalbas/rsmt2d" 12 | ) 13 | 14 | // ProbabilisticBlock represents a block designed for the Probabilistic Validity Rule. 15 | type ProbabilisticBlock struct { 16 | prevHash []byte 17 | messages []Message 18 | rowRoots [][]byte 19 | columnRoots [][]byte 20 | cachedRowRoots [][]byte 21 | cachedColumnRoots [][]byte 22 | squareWidth int 23 | headerOnly bool 24 | cachedEds *rsmt2d.ExtendedDataSquare 25 | messageSize int 26 | validated bool 27 | sampleRequest *SampleRequest 28 | provenDependencies map[string]bool 29 | } 30 | 31 | type SampleRequest struct { 32 | Indexes []int 33 | Axes []int 34 | } 35 | 36 | type SampleResponse struct { 37 | Proofs [][][]byte 38 | } 39 | 40 | // NewProbabilisticBlock returns a new probabilistic block. 41 | func NewProbabilisticBlock(prevHash []byte, messageSize int) Block { 42 | return &ProbabilisticBlock{ 43 | prevHash: prevHash, 44 | messageSize: messageSize, 45 | provenDependencies: make(map[string]bool), 46 | } 47 | } 48 | 49 | // ImportProbabilisticBlockBlockHeader imports a received probabilistic block without the messages. 50 | func ImportProbabilisticBlockHeader(prevHash []byte, rowRoots [][]byte, columnRoots [][]byte, squareWidth int, messageSize int, validated bool) Block { 51 | return &ProbabilisticBlock{ 52 | prevHash: prevHash, 53 | rowRoots: rowRoots, 54 | columnRoots: columnRoots, 55 | squareWidth: squareWidth, 56 | headerOnly: true, 57 | messageSize: messageSize, 58 | validated: validated, 59 | provenDependencies: make(map[string]bool), 60 | } 61 | } 62 | 63 | // ImportProbabilisticBlock imports a received probabilistic block. 64 | func ImportProbabilisticBlock(prevHash []byte, messages []Message, messageSize int, validated bool) Block { 65 | return &ProbabilisticBlock{ 66 | prevHash: prevHash, 67 | messages: messages, 68 | messageSize: messageSize, 69 | validated: validated, 70 | provenDependencies: make(map[string]bool), 71 | } 72 | } 73 | 74 | // SquareWidth returns the width of coded data square of the block. 75 | func (pb *ProbabilisticBlock) SquareWidth() int { 76 | if pb.headerOnly { 77 | return pb.squareWidth 78 | } else { 79 | return int(pb.eds().Width()) 80 | } 81 | } 82 | 83 | // AddMessage adds a message to the block. 84 | func (pb *ProbabilisticBlock) AddMessage(message Message) { 85 | pb.messages = append(pb.messages, message) 86 | pb.cachedEds = nil 87 | pb.cachedRowRoots = nil 88 | pb.cachedColumnRoots = nil 89 | } 90 | 91 | func (pb *ProbabilisticBlock) messagesBytes() [][]byte { 92 | messagesBytes := make([][]byte, len(pb.messages)) 93 | for index, message := range pb.messages { 94 | messagesBytes[index] = message.MarshalPadded(pb.messageSize) 95 | } 96 | return messagesBytes 97 | } 98 | 99 | func (pb *ProbabilisticBlock) eds() *rsmt2d.ExtendedDataSquare { 100 | if pb.cachedEds == nil { 101 | data := pb.messagesBytes() 102 | missingShares := int(math.Pow(math.Ceil(math.Sqrt(float64(len(data)))), 2)) - len(data) 103 | paddingShare := make([]byte, pb.messageSize) 104 | for i := 0; i < pb.messageSize; i++ { 105 | paddingShare[i] = 0xFF // this will ensure it will be treated like a redundancy share 106 | } 107 | for i := 0; i < missingShares; i++ { 108 | freshPaddingShare := make([]byte, pb.messageSize) 109 | copy(freshPaddingShare, paddingShare) 110 | data = append(data, freshPaddingShare) 111 | } 112 | pb.cachedEds, _ = rsmt2d.ComputeExtendedDataSquare(data, rsmt2d.RSGF8) 113 | } 114 | 115 | return pb.cachedEds 116 | } 117 | 118 | // RowRoots returns the Merkle roots of the rows of the block. 119 | func (pb *ProbabilisticBlock) RowRoots() [][]byte { 120 | if pb.rowRoots != nil { 121 | return pb.rowRoots 122 | } 123 | 124 | if pb.cachedRowRoots == nil { 125 | pb.computeRoots() 126 | } 127 | 128 | return pb.cachedRowRoots 129 | } 130 | 131 | // ColumnRoots returns the Merkle roots of the columns of the block. 132 | func (pb *ProbabilisticBlock) ColumnRoots() [][]byte { 133 | if pb.columnRoots != nil { 134 | return pb.columnRoots 135 | } 136 | 137 | if pb.cachedColumnRoots == nil { 138 | pb.computeRoots() 139 | } 140 | 141 | return pb.cachedColumnRoots 142 | } 143 | 144 | func (pb *ProbabilisticBlock) computeRoots() { 145 | ndf := NewNamespaceDummyFlagger() 146 | fh := NewFlagHasher(ndf, sha256.New()) 147 | rowRoots := make([][]byte, pb.SquareWidth()) 148 | columnRoots := make([][]byte, pb.SquareWidth()) 149 | var rowTree *merkletree.Tree 150 | var columnTree *merkletree.Tree 151 | var rowData [][]byte 152 | var columnData [][]byte 153 | for i := 0; i < pb.SquareWidth(); i++ { 154 | if i >= pb.SquareWidth() / 2 { 155 | fh.(*flagDigest).setCodedMode(true) 156 | } 157 | rowTree = merkletree.New(fh) 158 | columnTree = merkletree.New(fh) 159 | rowData = pb.eds().Row(uint(i)) 160 | columnData = pb.eds().Column(uint(i)) 161 | for j := 0; j < pb.SquareWidth(); j++ { 162 | if j >= pb.SquareWidth() / 2 { 163 | fh.(*flagDigest).setCodedMode(true) 164 | } 165 | rowTree.Push(rowData[j]) 166 | columnTree.Push(columnData[j]) 167 | } 168 | fh.(*flagDigest).setCodedMode(false) 169 | 170 | rowRoots[i] = rowTree.Root() 171 | columnRoots[i] = columnTree.Root() 172 | } 173 | 174 | pb.cachedRowRoots = rowRoots 175 | pb.cachedColumnRoots = columnRoots 176 | } 177 | 178 | func (pb *ProbabilisticBlock) RequestSamples(n int) (*SampleRequest, error) { 179 | indexes := make([]int, n) 180 | axes := make([]int, n) 181 | for i := 0; i < n; i++ { 182 | val, err := rand.Int(rand.Reader, big.NewInt(int64(math.Pow(float64(pb.SquareWidth()), 2)))) 183 | if err != nil { 184 | return nil, err 185 | } 186 | indexes[i] = int(val.Int64()) 187 | val, err = rand.Int(rand.Reader, big.NewInt(2)) 188 | if err != nil { 189 | return nil, err 190 | } 191 | axes[i] = int(val.Int64()) 192 | } 193 | 194 | pb.sampleRequest = &SampleRequest{ 195 | Indexes: indexes, 196 | Axes: axes, 197 | } 198 | 199 | return pb.sampleRequest, nil 200 | } 201 | 202 | func (pb *ProbabilisticBlock) RespondSamples(request *SampleRequest) *SampleResponse { 203 | var proofs [][][]byte 204 | ndf := NewNamespaceDummyFlagger() 205 | fh := NewFlagHasher(ndf, sha256.New()) 206 | for x, index := range request.Indexes { 207 | r, c := pb.shareIndexToCoordinates(index) 208 | 209 | // Add Merkle proof to response 210 | var data [][]byte 211 | tree := merkletree.New(fh) 212 | if request.Axes[x] == 0 { // row 213 | data = pb.eds().Row(uint(r)) 214 | tree.SetIndex(uint64(c)) 215 | if r >= pb.SquareWidth() / 2 { 216 | fh.(*flagDigest).setCodedMode(true) 217 | } 218 | } else { // column 219 | data = pb.eds().Column(uint(c)) 220 | tree.SetIndex(uint64(r)) 221 | if c >= pb.SquareWidth() / 2 { 222 | fh.(*flagDigest).setCodedMode(true) 223 | } 224 | } 225 | for j, share := range data { 226 | if j >= pb.SquareWidth() / 2 { 227 | fh.(*flagDigest).setCodedMode(true) 228 | } 229 | tree.Push(share) 230 | } 231 | fh.(*flagDigest).setCodedMode(false) 232 | _, proof, _, _ := tree.Prove() 233 | proofs = append(proofs, proof) 234 | } 235 | 236 | return &SampleResponse{ 237 | Proofs: proofs, 238 | } 239 | } 240 | 241 | func (pb *ProbabilisticBlock) ProcessSamplesResponse(response *SampleResponse) bool { 242 | if len(response.Proofs) != len(pb.sampleRequest.Indexes) { 243 | return false 244 | } 245 | 246 | ndf := NewNamespaceDummyFlagger() 247 | fh := NewFlagHasher(ndf, sha256.New()) 248 | for x, index := range pb.sampleRequest.Indexes { 249 | r, c := pb.shareIndexToCoordinates(index) 250 | var root []byte 251 | var result bool 252 | if r >= pb.SquareWidth() / 2 || c >= pb.SquareWidth() / 2 { 253 | fh.(*flagDigest).setCodedMode(true) 254 | } 255 | if pb.sampleRequest.Axes[x] == 0 { // row 256 | root = pb.RowRoots()[r] 257 | result = merkletree.VerifyProof(fh, root, response.Proofs[x], uint64(c), uint64(pb.SquareWidth())) 258 | } else { // column 259 | root = pb.ColumnRoots()[c] 260 | result = merkletree.VerifyProof(fh, root, response.Proofs[x], uint64(r), uint64(pb.SquareWidth())) 261 | } 262 | fh.(*flagDigest).setCodedMode(false) 263 | if !result { 264 | return false 265 | } 266 | } 267 | 268 | return true 269 | } 270 | 271 | // Digest computes the hash of the block. 272 | func (pb *ProbabilisticBlock) Digest() []byte { 273 | hasher := sha256.New() 274 | hasher.Write(pb.prevHash) 275 | for _, root := range pb.rowRoots { 276 | hasher.Write(root) 277 | } 278 | for _, root := range pb.columnRoots { 279 | hasher.Write(root) 280 | } 281 | return hasher.Sum(nil) 282 | } 283 | 284 | // Valid returns true if the block is valid. 285 | func (pb *ProbabilisticBlock) Valid() bool { 286 | return pb.validated 287 | } 288 | 289 | // PrevHash returns the hash of the previous block. 290 | func (pb *ProbabilisticBlock) PrevHash() []byte { 291 | return pb.prevHash 292 | } 293 | 294 | // Messages returns the block's messages. 295 | func (pb *ProbabilisticBlock) Messages() []Message { 296 | return pb.messages 297 | } 298 | 299 | func (pb *ProbabilisticBlock) indexToCoordinates(index int) (row, column int) { 300 | row = index / (pb.SquareWidth() / 2) 301 | column = index % (pb.SquareWidth() / 2) 302 | return 303 | } 304 | 305 | func (pb *ProbabilisticBlock) shareIndexToCoordinates(index int) (row, column int) { 306 | row = index / pb.SquareWidth() 307 | column = index % pb.SquareWidth() 308 | return 309 | } 310 | 311 | // ApplicationProof creates a Merkle proof for all of the messages in a block for an application namespace. 312 | // All proofs are created from row roots only. 313 | func (pb *ProbabilisticBlock) ApplicationProof(namespace [namespaceSize]byte) (int, int, [][][]byte, *[]Message, [][]byte) { 314 | var proofStart int 315 | var proofEnd int 316 | var found bool 317 | for index, message := range pb.messages { 318 | if message.Namespace() == namespace { 319 | if !found { 320 | found = true 321 | proofStart = index 322 | } 323 | proofEnd = index + 1 324 | } 325 | } 326 | 327 | var inRange bool 328 | if !found { 329 | var prevMessage Message 330 | // We need to generate a proof for an absence of relevant messages. 331 | for index, message := range pb.messages { 332 | if index != 0 { 333 | prevNs := prevMessage.Namespace() 334 | currentNs := message.Namespace() 335 | if ((bytes.Compare(prevNs[:], namespace[:]) < 0 && bytes.Compare(namespace[:], currentNs[:]) < 0) || 336 | (bytes.Compare(prevNs[:], namespace[:]) > 0 && bytes.Compare(namespace[:], currentNs[:]) > 0)) { 337 | if !inRange { 338 | inRange = true 339 | proofStart = index 340 | } 341 | proofEnd = index + 1 342 | } 343 | } 344 | prevMessage = message 345 | } 346 | } 347 | 348 | ndf := NewNamespaceDummyFlagger() 349 | fh := NewFlagHasher(ndf, sha256.New()) 350 | var proofs [][][]byte 351 | if found || inRange { 352 | proofStartRow, proofStartColumn := pb.indexToCoordinates(proofStart) 353 | proofEndRow, proofEndColumn := pb.indexToCoordinates(proofEnd) 354 | if proofEndColumn == 0 { 355 | proofEndRow -= 1 356 | proofEndColumn = pb.SquareWidth() / 2 357 | } 358 | for i := 0; i < pb.SquareWidth() / 2; i++ { 359 | if i >= proofStartRow && i <= proofEndRow { 360 | // This row needs Merkle proofs 361 | var startColumn int 362 | var endColumn int 363 | if i == proofStartRow { 364 | startColumn = proofStartColumn 365 | } else { 366 | startColumn = 0 367 | } 368 | if i == proofEndRow { 369 | endColumn = proofEndColumn 370 | } else { 371 | endColumn = pb.SquareWidth() / 2 372 | } 373 | rowProof, _ := merkletree.BuildRangeProof(startColumn, endColumn, NewCodedAxisSubtreeHasher(pb.eds().Row(uint(i)), fh)) 374 | proofs = append(proofs, rowProof) 375 | } 376 | } 377 | } 378 | proofMessages := pb.messages[proofStart:proofEnd] 379 | if found { 380 | return proofStart, proofEnd, proofs, &proofMessages, nil 381 | } 382 | 383 | var hashes [][]byte 384 | for _, message := range proofMessages { 385 | ndf := NewNamespaceDummyFlagger() 386 | fh := NewFlagHasher(ndf, sha256.New()) 387 | hashes = append(hashes, leafSum(fh, message.MarshalPadded(pb.messageSize))) 388 | fh.Reset() 389 | } 390 | 391 | return proofStart, proofEnd, proofs, nil, hashes 392 | } 393 | 394 | // VerifyApplicationProof verifies a Merkle proof for all of the messages in a block for an application namespace. 395 | func (pb *ProbabilisticBlock) VerifyApplicationProof(namespace [namespaceSize]byte, proofStart int, proofEnd int, proofs [][][]byte, messages *[]Message, hashes [][]byte) bool { 396 | // Verify Merkle proofs 397 | ndf := NewNamespaceDummyFlagger() 398 | fh := NewFlagHasher(ndf, sha256.New()) 399 | var lh merkletree.LeafHasher 400 | if messages != nil { 401 | lh = NewPaddedMessageLeafHasher(messages, fh, pb.messageSize) 402 | } else { 403 | lh = NewHashLeafHasher(hashes) 404 | } 405 | 406 | proofStartRow, proofStartColumn := pb.indexToCoordinates(proofStart) 407 | proofEndRow, proofEndColumn := pb.indexToCoordinates(proofEnd) 408 | if proofEndColumn == 0 { 409 | proofEndRow -= 1 410 | proofEndColumn = pb.SquareWidth() / 2 411 | } 412 | proofNum := 0 413 | for i := 0; i < pb.SquareWidth() / 2; i++ { 414 | if i >= proofStartRow && i <= proofEndRow { 415 | // This row has Merkle proofs 416 | var startColumn int 417 | var endColumn int 418 | if i == proofStartRow { 419 | startColumn = proofStartColumn 420 | } else { 421 | startColumn = 0 422 | } 423 | if i == proofEndRow { 424 | endColumn = proofEndColumn 425 | } else { 426 | endColumn = pb.SquareWidth() / 2 427 | } 428 | 429 | // Verify proof 430 | result, err := merkletree.VerifyRangeProof(lh, fh, startColumn, endColumn, proofs[proofNum], pb.RowRoots()[i]) 431 | if !result || err != nil { 432 | return false 433 | } 434 | 435 | // Verify completeness 436 | var leafIndex uint64 437 | var leftSubtrees [][]byte 438 | var rightSubtrees [][]byte 439 | proof := proofs[proofNum] 440 | consumeUntil := func(end uint64) error { 441 | for leafIndex != end && len(proof) > 0 { 442 | subtreeSize := nextSubtreeSize(leafIndex, end) 443 | leftSubtrees = append(leftSubtrees, proof[0]) 444 | proof = proof[1:] 445 | leafIndex += uint64(subtreeSize) 446 | } 447 | return nil 448 | } 449 | if err := consumeUntil(uint64(startColumn)); err != nil { 450 | return false 451 | } 452 | rightSubtrees = proof 453 | 454 | for _, subtree := range leftSubtrees { 455 | _, max := dummyNamespacesFromFlag(subtree) 456 | if bytes.Compare(max, namespace[:]) >= 0 { 457 | return false 458 | } 459 | } 460 | for _, subtree := range rightSubtrees { 461 | min, _ := dummyNamespacesFromFlag(subtree) 462 | if bytes.Compare(min, namespace[:]) <= 0 { 463 | return false 464 | } 465 | } 466 | 467 | proofNum += 1 468 | } 469 | } 470 | 471 | return true 472 | } 473 | 474 | func (pb *ProbabilisticBlock) ProveDependency(index int) ([]byte, [][]byte, error) { 475 | ndf := NewNamespaceDummyFlagger() 476 | fh := NewFlagHasher(ndf, sha256.New()) 477 | r, c := pb.indexToCoordinates(index) 478 | proof, err := merkletree.BuildRangeProof(c, c + 1, NewCodedAxisSubtreeHasher(pb.eds().Row(uint(r)), fh)) 479 | if err != nil { 480 | return nil, nil, err 481 | } 482 | return leafSum(fh, pb.messages[index].MarshalPadded(pb.messageSize)), proof, nil 483 | } 484 | 485 | func (pb *ProbabilisticBlock) VerifyDependency(index int, hash []byte, proof [][]byte) bool { 486 | ndf := NewNamespaceDummyFlagger() 487 | fh := NewFlagHasher(ndf, sha256.New()) 488 | lh := NewHashLeafHasher([][]byte{hash}) 489 | r, c := pb.indexToCoordinates(index) 490 | result, err := merkletree.VerifyRangeProof(lh, fh, c, c + 1, proof, pb.RowRoots()[r]) 491 | if result && err == nil { 492 | pb.provenDependencies[string(hash)] = true 493 | return true 494 | } 495 | return false 496 | } 497 | 498 | func (pb *ProbabilisticBlock) DependencyProven(hash []byte) bool { 499 | if value, ok := pb.provenDependencies[string(hash)]; ok { 500 | return value 501 | } 502 | return false 503 | } 504 | -------------------------------------------------------------------------------- /probabilisticblock_test.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestProbabilisticBlock(t *testing.T) { 8 | pb := NewProbabilisticBlock([]byte{0}, 512) 9 | 10 | pb.AddMessage(*NewMessage([namespaceSize]byte{0}, []byte("foo"))) 11 | pb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 12 | pb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 13 | pb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 14 | pb.AddMessage(*NewMessage([namespaceSize]byte{3}, []byte("foo"))) 15 | pb.AddMessage(*NewMessage([namespaceSize]byte{3}, []byte("foo"))) 16 | pb.AddMessage(*NewMessage([namespaceSize]byte{4}, []byte("foo"))) 17 | pb.AddMessage(*NewMessage([namespaceSize]byte{4}, []byte("foob"))) 18 | 19 | proofStart, proofEnd, proofs, messages, hashes := pb.(*ProbabilisticBlock).ApplicationProof([namespaceSize]byte{1}) 20 | if messages == nil { 21 | t.Error("ApplicationProof incorrectly returned no messages") 22 | } 23 | result := pb.(*ProbabilisticBlock).VerifyApplicationProof([namespaceSize]byte{1}, proofStart, proofEnd, proofs, messages, hashes) 24 | if !result { 25 | t.Error("VerifyApplicationProof incorrectly returned false") 26 | } 27 | 28 | proofStart, proofEnd, proofs, messages, hashes = pb.(*ProbabilisticBlock).ApplicationProof([namespaceSize]byte{1}) 29 | proofs[0][0][0] = 0xFF 30 | if messages == nil { 31 | t.Error("ApplicationProof incorrectly returned no messages") 32 | } 33 | result = pb.(*ProbabilisticBlock).VerifyApplicationProof([namespaceSize]byte{1}, proofStart, proofEnd, proofs, messages, hashes) 34 | if result { 35 | t.Error("VerifyApplicationProof incorrectly returned true") 36 | } 37 | 38 | proofStart, proofEnd, proofs, messages, hashes = pb.(*ProbabilisticBlock).ApplicationProof([namespaceSize]byte{2}) 39 | if messages != nil { 40 | t.Error("ApplicationProof incorrectly returned messages") 41 | } 42 | result = pb.(*ProbabilisticBlock).VerifyApplicationProof([namespaceSize]byte{2}, proofStart, proofEnd, proofs, messages, hashes) 43 | if !result { 44 | t.Error("VerifyApplicationProof incorrectly returned false") 45 | } 46 | 47 | proofStart, proofEnd, proofs, messages, hashes = pb.(*ProbabilisticBlock).ApplicationProof([namespaceSize]byte{2}) 48 | proofs[0][0][0] = 0xFF 49 | if messages != nil { 50 | t.Error("ApplicationProof incorrectly returned messages") 51 | } 52 | result = pb.(*ProbabilisticBlock).VerifyApplicationProof([namespaceSize]byte{2}, proofStart, proofEnd, proofs, messages, hashes) 53 | if result { 54 | t.Error("VerifyApplicationProof incorrectly returned true") 55 | } 56 | } 57 | 58 | func TestProbabilisticBlockValidity(t *testing.T) { 59 | pb := NewProbabilisticBlock([]byte{0}, 512) 60 | 61 | pb.AddMessage(*NewMessage([namespaceSize]byte{0}, []byte("foo"))) 62 | pb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 63 | pb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 64 | pb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 65 | pb.AddMessage(*NewMessage([namespaceSize]byte{3}, []byte("foo"))) 66 | pb.AddMessage(*NewMessage([namespaceSize]byte{3}, []byte("foo"))) 67 | pb.AddMessage(*NewMessage([namespaceSize]byte{4}, []byte("foo"))) 68 | pb.AddMessage(*NewMessage([namespaceSize]byte{4}, []byte("foob"))) 69 | 70 | request, _ := pb.(*ProbabilisticBlock).RequestSamples(20) 71 | if len(request.Indexes) != 20 || len(request.Axes) != 20 { 72 | t.Error("sample request didn't return enough samples") 73 | } 74 | 75 | response := pb.(*ProbabilisticBlock).RespondSamples(request) 76 | if !pb.(*ProbabilisticBlock).ProcessSamplesResponse(response) { 77 | t.Error("processing of samples response incorrectly returned false") 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /simpleblock.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "bytes" 5 | "crypto/sha256" 6 | 7 | "gitlab.com/NebulousLabs/merkletree" 8 | ) 9 | 10 | // SimpleBlock represents a block designed for the Simple Validity Rule. 11 | type SimpleBlock struct { 12 | prevHash []byte 13 | messages []Message 14 | messagesRoot []byte 15 | provenDependencies map[string]bool 16 | } 17 | 18 | // NewSimpleBlock returns a new simple block. 19 | func NewSimpleBlock(prevHash []byte) Block { 20 | return &SimpleBlock{ 21 | prevHash: prevHash, 22 | provenDependencies: make(map[string]bool), 23 | } 24 | } 25 | 26 | // ImportSimpleBlockHeader imports a received simple block without the messages. 27 | func ImportSimpleBlockHeader(prevHash []byte, messagesRoot []byte) Block { 28 | return &SimpleBlock{ 29 | prevHash: prevHash, 30 | messagesRoot: messagesRoot, 31 | provenDependencies: make(map[string]bool), 32 | } 33 | } 34 | 35 | // ImportSimpleBlock imports a received simple block. 36 | func ImportSimpleBlock(prevHash []byte, messages []Message) Block { 37 | return &SimpleBlock{ 38 | prevHash: prevHash, 39 | messages: messages, 40 | provenDependencies: make(map[string]bool), 41 | } 42 | } 43 | 44 | // AddMessage adds a message to the block. 45 | func (sb *SimpleBlock) AddMessage(message Message) { 46 | sb.messages = append(sb.messages, message) 47 | 48 | // Force recompututation of messagesRoot 49 | sb.messagesRoot = nil 50 | } 51 | 52 | // MessagesRoot returns the Merkle root of the messages in the block. 53 | func (sb *SimpleBlock) MessagesRoot() []byte { 54 | if sb.messagesRoot == nil { 55 | ndf := NewNamespaceDummyFlagger() 56 | fh := NewFlagHasher(ndf, sha256.New()) 57 | tree := merkletree.New(fh) 58 | for _, message := range sb.messages { 59 | tree.Push(message.Marshal()) 60 | } 61 | sb.messagesRoot = tree.Root() 62 | } 63 | 64 | return sb.messagesRoot 65 | } 66 | 67 | // Digest computes the hash of the block. 68 | func (sb *SimpleBlock) Digest() []byte { 69 | hasher := sha256.New() 70 | hasher.Write(sb.prevHash) 71 | hasher.Write(sb.MessagesRoot()) 72 | return hasher.Sum(nil) 73 | } 74 | 75 | // Valid returns true if the block is valid. 76 | func (sb *SimpleBlock) Valid() bool { 77 | if sb.messages == nil { 78 | // Cannot validate block without messages. 79 | return false 80 | } 81 | 82 | ndf := NewNamespaceDummyFlagger() 83 | fh := NewFlagHasher(ndf, sha256.New()) 84 | tree := merkletree.New(fh) 85 | for _, message := range sb.messages { 86 | tree.Push(message.Marshal()) 87 | } 88 | if bytes.Compare(tree.Root(), sb.MessagesRoot()) == 0 { 89 | return true 90 | } 91 | return false 92 | } 93 | 94 | // PrevHash returns the hash of the previous block. 95 | func (sb *SimpleBlock) PrevHash() []byte { 96 | return sb.prevHash 97 | } 98 | 99 | // Messages returns the block's messages. 100 | func (sb *SimpleBlock) Messages() []Message { 101 | return sb.messages 102 | } 103 | 104 | // ApplicationProof creates a Merkle proof for all of the messages in a block for an application namespace. 105 | func (sb *SimpleBlock) ApplicationProof(namespace [namespaceSize]byte) (int, int, [][]byte, *[]Message, [][]byte) { 106 | var proofStart int 107 | var proofEnd int 108 | var found bool 109 | for index, message := range sb.messages { 110 | if message.Namespace() == namespace { 111 | if !found { 112 | found = true 113 | proofStart = index 114 | } 115 | proofEnd = index + 1 116 | } 117 | } 118 | 119 | var inRange bool 120 | if !found { 121 | var prevMessage Message 122 | // We need to generate a proof for an absence of relevant messages. 123 | for index, message := range sb.messages { 124 | if index != 0 { 125 | prevNs := prevMessage.Namespace() 126 | currentNs := message.Namespace() 127 | if ((bytes.Compare(prevNs[:], namespace[:]) < 0 && bytes.Compare(namespace[:], currentNs[:]) < 0) || 128 | (bytes.Compare(prevNs[:], namespace[:]) > 0 && bytes.Compare(namespace[:], currentNs[:]) > 0)) { 129 | if !inRange { 130 | inRange = true 131 | proofStart = index 132 | } 133 | proofEnd = index + 1 134 | } 135 | } 136 | prevMessage = message 137 | } 138 | } 139 | 140 | ndf := NewNamespaceDummyFlagger() 141 | fh := NewFlagHasher(ndf, sha256.New()) 142 | var proof [][]byte 143 | if found || inRange { 144 | proof, _ = merkletree.BuildRangeProof(proofStart, proofEnd, NewMessageSubtreeHasher(&sb.messages, fh)) 145 | } 146 | proofMessages := sb.messages[proofStart:proofEnd] 147 | if found { 148 | return proofStart, proofEnd, proof, &proofMessages, nil 149 | } 150 | 151 | var hashes [][]byte 152 | for _, message := range proofMessages { 153 | ndf := NewNamespaceDummyFlagger() 154 | fh := NewFlagHasher(ndf, sha256.New()) 155 | hashes = append(hashes, leafSum(fh, message.Marshal())) 156 | fh.Reset() 157 | } 158 | 159 | return proofStart, proofEnd, proof, nil, hashes 160 | } 161 | 162 | // VerifyApplicationProof verifies a Merkle proof for all of the messages in a block for an application namespace. 163 | func (sb *SimpleBlock) VerifyApplicationProof(namespace [namespaceSize]byte, proofStart int, proofEnd int, proof [][]byte, messages *[]Message, hashes [][]byte) bool { 164 | // Verify Merkle proof 165 | ndf := NewNamespaceDummyFlagger() 166 | fh := NewFlagHasher(ndf, sha256.New()) 167 | var lh merkletree.LeafHasher 168 | if messages != nil { 169 | lh = NewMessageLeafHasher(messages, fh) 170 | } else { 171 | lh = NewHashLeafHasher(hashes) 172 | } 173 | result, err := merkletree.VerifyRangeProof(lh, fh, proofStart, proofEnd, proof, sb.MessagesRoot()) 174 | if !result || err != nil { 175 | return false 176 | } 177 | 178 | // Verify proof completeness 179 | var leafIndex uint64 180 | var leftSubtrees [][]byte 181 | var rightSubtrees [][]byte 182 | consumeUntil := func(end uint64) error { 183 | for leafIndex != end && len(proof) > 0 { 184 | subtreeSize := nextSubtreeSize(leafIndex, end) 185 | leftSubtrees = append(leftSubtrees, proof[0]) 186 | proof = proof[1:] 187 | leafIndex += uint64(subtreeSize) 188 | } 189 | return nil 190 | } 191 | if err := consumeUntil(uint64(proofStart)); err != nil { 192 | return false 193 | } 194 | rightSubtrees = proof 195 | 196 | for _, subtree := range leftSubtrees { 197 | _, max := dummyNamespacesFromFlag(subtree) 198 | if bytes.Compare(max, namespace[:]) >= 0 { 199 | return false 200 | } 201 | } 202 | for _, subtree := range rightSubtrees { 203 | min, _ := dummyNamespacesFromFlag(subtree) 204 | if bytes.Compare(min, namespace[:]) <= 0 { 205 | return false 206 | } 207 | } 208 | 209 | return true 210 | } 211 | 212 | func (sb *SimpleBlock) ProveDependency(index int) ([]byte, [][]byte, error) { 213 | ndf := NewNamespaceDummyFlagger() 214 | fh := NewFlagHasher(ndf, sha256.New()) 215 | proof, err := merkletree.BuildRangeProof(index, index + 1, NewMessageSubtreeHasher(&sb.messages, fh)) 216 | if err != nil { 217 | return nil, nil, err 218 | } 219 | return leafSum(fh, sb.messages[index].Marshal()), proof, nil 220 | } 221 | 222 | func (sb *SimpleBlock) VerifyDependency(index int, hash []byte, proof [][]byte) bool { 223 | ndf := NewNamespaceDummyFlagger() 224 | fh := NewFlagHasher(ndf, sha256.New()) 225 | lh := NewHashLeafHasher([][]byte{hash}) 226 | result, err := merkletree.VerifyRangeProof(lh, fh, index, index + 1, proof, sb.MessagesRoot()) 227 | if result && err == nil { 228 | sb.provenDependencies[string(hash)] = true 229 | return true 230 | } 231 | return false 232 | } 233 | 234 | func (sb *SimpleBlock) DependencyProven(hash []byte) bool { 235 | if value, ok := sb.provenDependencies[string(hash)]; ok { 236 | return value 237 | } 238 | return false 239 | } 240 | -------------------------------------------------------------------------------- /simpleblock_test.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSimpleBlock(t *testing.T) { 8 | sb := NewSimpleBlock([]byte{0}) 9 | 10 | sb.AddMessage(*NewMessage([namespaceSize]byte{0}, []byte("foo"))) 11 | sb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 12 | sb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 13 | sb.AddMessage(*NewMessage([namespaceSize]byte{1}, []byte("foo"))) 14 | sb.AddMessage(*NewMessage([namespaceSize]byte{3}, []byte("foo"))) 15 | sb.AddMessage(*NewMessage([namespaceSize]byte{3}, []byte("foo"))) 16 | sb.AddMessage(*NewMessage([namespaceSize]byte{4}, []byte("foo"))) 17 | 18 | proofStart, proofEnd, proof, messages, hashes := sb.(*SimpleBlock).ApplicationProof([namespaceSize]byte{1}) 19 | if messages == nil { 20 | t.Error("ApplicationProof incorrectly returned no messages") 21 | } 22 | result := sb.(*SimpleBlock).VerifyApplicationProof([namespaceSize]byte{1}, proofStart, proofEnd, proof, messages, hashes) 23 | if !result { 24 | t.Error("VerifyApplicationProof incorrectly returned false") 25 | } 26 | 27 | proofStart, proofEnd, proof, messages, hashes = sb.(*SimpleBlock).ApplicationProof([namespaceSize]byte{1}) 28 | proof[0][0] = 0xFF 29 | if messages == nil { 30 | t.Error("ApplicationProof incorrectly returned no messages") 31 | } 32 | result = sb.(*SimpleBlock).VerifyApplicationProof([namespaceSize]byte{1}, proofStart, proofEnd, proof, messages, hashes) 33 | if result { 34 | t.Error("VerifyApplicationProof incorrectly returned true") 35 | } 36 | 37 | proofStart, proofEnd, proof, messages, hashes = sb.(*SimpleBlock).ApplicationProof([namespaceSize]byte{2}) 38 | if messages != nil { 39 | t.Error("ApplicationProof incorrectly returned messages") 40 | } 41 | result = sb.(*SimpleBlock).VerifyApplicationProof([namespaceSize]byte{2}, proofStart, proofEnd, proof, messages, hashes) 42 | if !result { 43 | t.Error("VerifyApplicationProof incorrectly returned false") 44 | } 45 | 46 | proofStart, proofEnd, proof, messages, hashes = sb.(*SimpleBlock).ApplicationProof([namespaceSize]byte{2}) 47 | proof[0][0] = 0xFF 48 | if messages != nil { 49 | t.Error("ApplicationProof incorrectly returned messages") 50 | } 51 | result = sb.(*SimpleBlock).VerifyApplicationProof([namespaceSize]byte{2}, proofStart, proofEnd, proof, messages, hashes) 52 | if result { 53 | t.Error("VerifyApplicationProof incorrectly returned true") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /todos.txt: -------------------------------------------------------------------------------- 1 | - benchmarks 2 | -------------------------------------------------------------------------------- /treehashers.go: -------------------------------------------------------------------------------- 1 | package lazyledger 2 | 3 | import ( 4 | "hash" 5 | "io" 6 | 7 | "gitlab.com/NebulousLabs/merkletree" 8 | ) 9 | 10 | // MessageSubtreeHasher implements merkletree.SubtreeHasher by reading leaf data from an underlying slice of messages. 11 | type MessageSubtreeHasher struct { 12 | messages *[]Message 13 | h hash.Hash 14 | pos int 15 | } 16 | 17 | // NewMessageSubtreeHasher returns a new MessageSubtreeHasher for a pointer to a slice of messages. 18 | func NewMessageSubtreeHasher(messages *[]Message, h hash.Hash) *MessageSubtreeHasher { 19 | return &MessageSubtreeHasher{ 20 | messages: messages, 21 | h: h, 22 | } 23 | } 24 | 25 | // NextSubtreeRoot implements SubtreeHasher. 26 | func (msh *MessageSubtreeHasher) NextSubtreeRoot(subtreeSize int) ([]byte, error) { 27 | tree := merkletree.New(msh.h) 28 | for i := 0; i < subtreeSize; i++ { 29 | if msh.pos >= len(*msh.messages) { 30 | break 31 | } 32 | tree.Push((*msh.messages)[msh.pos].Marshal()) 33 | msh.pos += 1 34 | } 35 | 36 | root := tree.Root() 37 | if root == nil { 38 | return nil, io.EOF 39 | } 40 | 41 | return root, nil 42 | } 43 | 44 | // Skip implements SubtreeHasher. 45 | func (msh *MessageSubtreeHasher) Skip(n int) error { 46 | if msh.pos + n > len(*msh.messages) { 47 | msh.pos = len(*msh.messages) 48 | return io.ErrUnexpectedEOF 49 | } 50 | msh.pos += n 51 | return nil 52 | } 53 | 54 | // MessageLeafHasher implements the merkletree.LeafHasher interface by reading leaf data from an underlying slice of messages. 55 | type MessageLeafHasher struct { 56 | messages *[]Message 57 | h hash.Hash 58 | pos int 59 | messageSize int 60 | } 61 | 62 | // NewMessageLeafHasher returns a new MessageLeafHasher for a pointer to a slice of messages. 63 | func NewMessageLeafHasher(messages *[]Message, h hash.Hash) *MessageLeafHasher { 64 | return &MessageLeafHasher{ 65 | messages: messages, 66 | h: h, 67 | } 68 | } 69 | 70 | // NewPaddedMessageLeafHasher returns a new MessageLeafHasher for a pointer to a slice of messages, where messages should be padded. 71 | func NewPaddedMessageLeafHasher(messages *[]Message, h hash.Hash, messageSize int) *MessageLeafHasher { 72 | return &MessageLeafHasher{ 73 | messages: messages, 74 | h: h, 75 | messageSize: messageSize, 76 | } 77 | } 78 | 79 | // NextLeafHash implements LeafHasher 80 | func (mlh *MessageLeafHasher) NextLeafHash() (leafHash []byte, err error) { 81 | if mlh.pos >= len(*mlh.messages) { 82 | return nil, io.EOF 83 | } 84 | 85 | if mlh.messageSize > 0 { 86 | leafHash = leafSum(mlh.h, (*mlh.messages)[mlh.pos].MarshalPadded(mlh.messageSize)) 87 | } else { 88 | leafHash = leafSum(mlh.h, (*mlh.messages)[mlh.pos].Marshal()) 89 | } 90 | err = nil 91 | mlh.pos += 1 92 | return 93 | } 94 | 95 | // HashLeafHasher implements the merkletree.LeafHasher interface from a slice of hashes. 96 | type HashLeafHasher struct { 97 | hashes [][]byte 98 | pos int 99 | } 100 | 101 | // NewHashLeafHasher returns a new MessageLeafHasher for a slice of hashes. 102 | func NewHashLeafHasher(hashes [][]byte) *HashLeafHasher { 103 | return &HashLeafHasher{ 104 | hashes: hashes, 105 | } 106 | } 107 | 108 | // NextLeafHash implements LeafHasher 109 | func (hlh *HashLeafHasher) NextLeafHash() (leafHash []byte, err error) { 110 | if hlh.pos >= len(hlh.hashes) { 111 | return nil, io.EOF 112 | } 113 | 114 | leafHash = hlh.hashes[hlh.pos] 115 | err = nil 116 | hlh.pos += 1 117 | return 118 | } 119 | 120 | // BytesLeafHasher implements the merkletree.LeafHasher interface by reading a slice. 121 | type BytesLeafHasher struct { 122 | data [][]byte 123 | h hash.Hash 124 | pos int 125 | } 126 | 127 | // NewBytesLeafHasher returns a new BytesLeafHasher for a slice. 128 | func NewBytesLeafHasher(data [][]byte, h hash.Hash) *BytesLeafHasher { 129 | return &BytesLeafHasher{ 130 | data: data, 131 | h: h, 132 | } 133 | } 134 | 135 | // NextLeafHash implements LeafHasher 136 | func (blh *BytesLeafHasher) NextLeafHash() (leafHash []byte, err error) { 137 | if blh.pos >= len(blh.data) { 138 | return nil, io.EOF 139 | } 140 | 141 | leafHash = leafSum(blh.h, blh.data[blh.pos]) 142 | err = nil 143 | blh.pos += 1 144 | return 145 | } 146 | 147 | //CodedAxisSubtreeHasher implements merkletree.SubtreeHasher by reading leaf data from an underlying slice representing a coded axis. 148 | type CodedAxisSubtreeHasher struct { 149 | data [][]byte 150 | h hash.Hash 151 | pos int 152 | } 153 | 154 | // NewCodedAxisSubtreeHasher returns a new CodedAxisSubtreeHasher for a pointer to a slice representing a coded axis. 155 | func NewCodedAxisSubtreeHasher(data [][]byte, h hash.Hash) *CodedAxisSubtreeHasher { 156 | return &CodedAxisSubtreeHasher{ 157 | data: data, 158 | h: h, 159 | } 160 | } 161 | 162 | // NextSubtreeRoot implements SubtreeHasher. 163 | func (cash *CodedAxisSubtreeHasher) NextSubtreeRoot(subtreeSize int) ([]byte, error) { 164 | tree := merkletree.New(cash.h) 165 | for i := 0; i < subtreeSize; i++ { 166 | if cash.pos >= len(cash.data) { 167 | break 168 | } 169 | if cash.pos >= len(cash.data) / 2 { 170 | cash.h.(*flagDigest).setCodedMode(true) 171 | } 172 | tree.Push(cash.data[cash.pos]) 173 | cash.h.(*flagDigest).setCodedMode(false) 174 | cash.pos += 1 175 | } 176 | 177 | root := tree.Root() 178 | if root == nil { 179 | return nil, io.EOF 180 | } 181 | 182 | return root, nil 183 | } 184 | 185 | // Skip implements SubtreeHasher. 186 | func (cash *CodedAxisSubtreeHasher) Skip(n int) error { 187 | if cash.pos + n > len(cash.data) { 188 | cash.pos = len(cash.data) 189 | return io.ErrUnexpectedEOF 190 | } 191 | cash.pos += n 192 | return nil 193 | } 194 | --------------------------------------------------------------------------------