|
| 1 | +# Ledger Lite |
| 2 | + |
| 3 | +ledger-lite library / gem - hyper ledger book for the distributed blockchain internet era; add your transactions one block at a time; transfer crypto(currencie)s and balance the accounts |
| 4 | + |
| 5 | + |
| 6 | +* home :: [github.com/openblockchains/ledger.lite.rb](https://github.com/openblockchains/ledger.lite.rb) |
| 7 | +* bugs :: [github.com/openblockchains/ledger.lite.rb/issues](https://github.com/openblockchains/ledger.lite.rb/issues) |
| 8 | +* gem :: [rubygems.org/gems/ledger-lite](https://rubygems.org/gems/ledger-lite) |
| 9 | +* rdoc :: [rubydoc.info/gems/ledger-lite](http://rubydoc.info/gems/ledger-lite) |
| 10 | + |
| 11 | + |
| 12 | + |
| 13 | +## Usage |
| 14 | + |
| 15 | +Let's add some transactions to the (hyper) ledger book: |
| 16 | + |
| 17 | +| From | To | $ | |
| 18 | +|---------------------------|--------------|----:| |
| 19 | +| Central Bank (†) | Vincent | 11 | |
| 20 | +| Vincent | Anne | 3 | |
| 21 | +| Anne | Julia | 2 | |
| 22 | +| Julia | Luuk | 1 | |
| 23 | +| | | | |
| 24 | +| De Nederlandsche Bank (†) | Ruben | 11 | |
| 25 | +| Vincent | Max | 3 | |
| 26 | +| Ruben | Julia | 2 | |
| 27 | +| Anne | Martijn | 1 | |
| 28 | + |
| 29 | +(†): Mining Transaction - New Dutch Gulden ($) on the Market! |
| 30 | + |
| 31 | + |
| 32 | + |
| 33 | +### Use `send` (send payment/transfer money) |
| 34 | + |
| 35 | +```ruby |
| 36 | + |
| 37 | +ledger = Ledger.new |
| 38 | + |
| 39 | +ledger.send( "Central Bank†", "Vincent", 11 ) |
| 40 | +ledger.send( "Vincent", "Anne", 3 ) |
| 41 | +ledger.send( "Anne", "Julia", 2 ) |
| 42 | +ledger.send( "Julia", "Luuk", 1 ) |
| 43 | + |
| 44 | +ledger.send( "De Nederlandsche Bank†", "Ruben", 11 ) |
| 45 | +ledger.send( "Vincent", "Max", 3 ) |
| 46 | +ledger.send( "Ruben", "Julia", 2 ) |
| 47 | +ledger.send( "Anne", "Martijn", 1 ) |
| 48 | + |
| 49 | +pp ledger ## pp = pretty print |
| 50 | + |
| 51 | +``` |
| 52 | + |
| 53 | +resulting in |
| 54 | + |
| 55 | +``` |
| 56 | +#<LedgerLite::Ledger |
| 57 | + @addr={ |
| 58 | + "Vincent" => 5, |
| 59 | + "Anne" => 0, |
| 60 | + "Julia" => 3, |
| 61 | + "Luuk" => 1, |
| 62 | + "Ruben" => 9, |
| 63 | + "Max" => 3, |
| 64 | + "Martijn" => 1}> |
| 65 | +``` |
| 66 | + |
| 67 | +that is, the addr hash holds all addresses (addr) with the account balances |
| 68 | +telling you who owns how much: |
| 69 | + |
| 70 | +| Addr(ess) | Balance $ | |
| 71 | +|---------------------|----------:| |
| 72 | +| Vincent | 5 | |
| 73 | +| Anne | 0 | |
| 74 | +| Julia | 3 | |
| 75 | +| Luuk | 1 | |
| 76 | +| Ruben | 9 | |
| 77 | +| Max | 3 | |
| 78 | +| Martijn | 1 | |
| 79 | + |
| 80 | + |
| 81 | +### Use `write` to write / add transactions |
| 82 | + |
| 83 | + |
| 84 | +Or use transaction hashes: |
| 85 | + |
| 86 | +``` ruby |
| 87 | +ledger = Ledger.new |
| 88 | + |
| 89 | +ledger.write( from: "Central Bank†", to: "Vincent", amount: 11 ) |
| 90 | +ledger.write( from: "Vincent", to: "Anne", amount: 3 ) |
| 91 | +ledger.write( from: "Anne", to: "Julia", amount: 2 ) |
| 92 | +ledger.write( from: "Julia", to: "Luuk", amount: 1 ) |
| 93 | + |
| 94 | +ledger.write( { from: "De Nederlandsche Bank†", to: "Ruben", amount: 11 }, |
| 95 | + { from: "Vincent", to: "Max", amount: 3 }, |
| 96 | + { from: "Ruben", to: "Julia", amount: 2 }, |
| 97 | + { from: "Anne", to: "Martijn", amount: 1 } ) |
| 98 | + |
| 99 | +pp ledger |
| 100 | +``` |
| 101 | + |
| 102 | +Or use transaction (tx) classes/structs: |
| 103 | + |
| 104 | +``` ruby |
| 105 | +ledger = Ledger.new |
| 106 | + |
| 107 | +ledger.write( Tx.new( "Central Bank†", "Vincent", 11 )) |
| 108 | +ledger.write( Tx.new( "Vincent", "Anne", 3 )) |
| 109 | +ledger.write( Tx.new( "Anne", "Julia", 2 )) |
| 110 | +ledger.write( Tx.new( "Julia", "Luuk", 1 )) |
| 111 | + |
| 112 | +ledger.write( Tx.new( "De Nederlandsche Bank†", "Ruben", 11 ), |
| 113 | + Tx.new( "Vincent", "Max", 3 ), |
| 114 | + Tx.new( "Ruben", "Julia", 2 ), |
| 115 | + Tx.new( "Anne", "Martijn", 1 )) |
| 116 | + |
| 117 | +pp ledger |
| 118 | +``` |
| 119 | + |
| 120 | +Or use the operator `<<` alias for `write`: |
| 121 | + |
| 122 | +```ruby |
| 123 | +ledger = Ledger.new |
| 124 | + |
| 125 | +ledger << Tx.new( "Central Bank†", "Vincent", 11 ) |
| 126 | +ledger << Tx.new( "Vincent", "Anne", 3 ) |
| 127 | +ledger << Tx.new( "Anne", "Julia", 2 ) |
| 128 | +ledger << Tx.new( "Julia", "Luuk", 1 ) |
| 129 | + |
| 130 | +ledger << [Tx.new( "De Nederlandsche Bank†", "Ruben", 11 ), |
| 131 | + Tx.new( "Vincent", "Max", 3 ), |
| 132 | + Tx.new( "Ruben", "Julia", 2 ), |
| 133 | + Tx.new( "Anne", "Martijn", 1 )] |
| 134 | + |
| 135 | +pp ledger |
| 136 | +``` |
| 137 | + |
| 138 | + |
| 139 | +Or use blocks of transaction hashes: |
| 140 | + |
| 141 | +``` ruby |
| 142 | + |
| 143 | +ledger = Ledger.new |
| 144 | + |
| 145 | +ledger.write( Block.new( { from: "Central Bank†", to: "Vincent", amount: 11 }, |
| 146 | + { from: "Vincent", to: "Anne", amount: 3 }, |
| 147 | + { from: "Anne", to: "Julia", amount: 2 }, |
| 148 | + { from: "Julia", to: "Luuk", amount: 1 } ), |
| 149 | + Block.new( { from: "De Nederlandsche Bank†", to: "Ruben", amount: 11 }, |
| 150 | + { from: "Vincent", to: "Max", amount: 3 }, |
| 151 | + { from: "Ruben", to: "Julia", amount: 2 }, |
| 152 | + { from: "Anne", to: "Martijn", amount: 1 } )) |
| 153 | + |
| 154 | +pp ledger |
| 155 | +``` |
| 156 | + |
| 157 | + |
| 158 | +Or use blocks of transaction classes/structs: |
| 159 | + |
| 160 | +``` ruby |
| 161 | +ledger = Ledger.new |
| 162 | + |
| 163 | +ledger.write( Block.new( Tx.new( "Central Bank†", "Vincent", 11 ), |
| 164 | + Tx.new( "Vincent", "Anne", 3 ), |
| 165 | + Tx.new( "Anne", "Julia", 2 ), |
| 166 | + Tx.new( "Julia", "Luuk", 1 )), |
| 167 | + Block.new( Tx.new( "De Nederlandsche Bank†", "Ruben", 11 ), |
| 168 | + Tx.new( "Vincent", "Max", 3 ), |
| 169 | + Tx.new( "Ruben", "Julia", 2 ), |
| 170 | + Tx.new( "Anne", "Martijn", 1 ))) |
| 171 | + |
| 172 | +pp ledger |
| 173 | +``` |
| 174 | + |
| 175 | +Or use blocks of transaction classes/structs (with keyword arguments): |
| 176 | + |
| 177 | +```ruby |
| 178 | +ledger = Ledger.new |
| 179 | + |
| 180 | +ledger.write( Block.new( Tx.new( from: "Central Bank†", to: "Vincent", amount: 11 ), |
| 181 | + Tx.new( from: "Vincent", to: "Anne", amount: 3 ), |
| 182 | + Tx.new( from: "Anne", to: "Julia", amount: 2 ), |
| 183 | + Tx.new( from: "Julia", to: "Luuk", amount: 1 )), |
| 184 | + Block.new( Tx.new( from: "De Nederlandsche Bank†", to: "Ruben", amount: 11 ), |
| 185 | + Tx.new( from: "Vincent", to: "Max", amount: 3 ), |
| 186 | + Tx.new( from: "Ruben", to: "Julia", amount: 2 ), |
| 187 | + Tx.new( from: "Anne", to: "Martijn", amount: 1 ))) |
| 188 | + |
| 189 | +pp ledger |
| 190 | +``` |
| 191 | + |
| 192 | +# Bonus: Track Commodities, Collectibles or Assets |
| 193 | + |
| 194 | +Ledger Lite lets you design / create your own transactions. For example, let's use |
| 195 | +`from`, `to`, `qty` (quantity) and `name` (of commodity, collectible or asset). |
| 196 | +Override the `Ledger#unpack` method for "unpacking" arguments from transactions |
| 197 | +and the `Ledger#send` method for "committing" transactions: |
| 198 | + |
| 199 | + |
| 200 | +``` ruby |
| 201 | +def unpack( tx ) |
| 202 | + ## "unpack" from, to, qty, name values |
| 203 | + if tx.is_a?( Hash ) ## support hashes |
| 204 | + from = tx[:from] |
| 205 | + to = tx[:to] |
| 206 | + qty = tx[:qty] |
| 207 | + name = tx[:name] |
| 208 | + else ## assume it's a transaction (tx) struct/class |
| 209 | + from = tx.from |
| 210 | + to = tx.to |
| 211 | + qty = tx.qty |
| 212 | + name = tx.name |
| 213 | + end |
| 214 | + [from,to,qty,name] |
| 215 | +end |
| 216 | +``` |
| 217 | + |
| 218 | +and |
| 219 | + |
| 220 | +``` ruby |
| 221 | +def send( from, to, qty, name ) |
| 222 | + if sufficient?( from, qty, name ) |
| 223 | + if self.class.config.coinbase?( from ) |
| 224 | + # note: coinbase has unlimited supply!! magic happens here |
| 225 | + else |
| 226 | + @addr[ from ][ name ] -= qty |
| 227 | + end |
| 228 | + @addr[ to ] ||= {} ## make sure addr exists (e.g. init with empty hash {}) |
| 229 | + @addr[ to ][ name ] ||= 0 |
| 230 | + @addr[ to ][ name ] += qty |
| 231 | + end |
| 232 | +end |
| 233 | +``` |
| 234 | + |
| 235 | +Now use the ledger with the new transaction format like: |
| 236 | + |
| 237 | + |
| 238 | +``` ruby |
| 239 | +ledger = Ledger.new |
| 240 | + |
| 241 | +ledger.send( "Keukenhof†", "Vincent", 11, "Tulip Admiral van Eijck" ) |
| 242 | +ledger.send( "Vincent", "Anne", 3, "Tulip Admiral van Eijck" ) |
| 243 | +ledger.send( "Anne", "Julia", 2, "Tulip Admiral van Eijck" ) |
| 244 | +ledger.send( "Julia", "Luuk", 1, "Tulip Admiral van Eijck" ) |
| 245 | + |
| 246 | +ledger.send( "Dutchgrown†", "Ruben", 11, "Tulip Semper Augustus" ) |
| 247 | +ledger.send( "Vincent", "Max", 3, "Tulip Admiral van Eijck" ) |
| 248 | +ledger.send( "Ruben", "Julia", 2, "Tulip Semper Augustus" ) |
| 249 | +ledger.send( "Anne", "Martijn", 1, "Tulip Admiral van Eijck" ) |
| 250 | + |
| 251 | +pp ledger ## pp == pretty print |
| 252 | +``` |
| 253 | + |
| 254 | +resulting in: |
| 255 | + |
| 256 | +``` |
| 257 | +#<LedgerLite::Ledger |
| 258 | + @addr={ |
| 259 | + "Vincent" => {"Tulip Admiral van Eijck" => 5}, |
| 260 | + "Anne" => {"Tulip Admiral van Eijck" => 0}, |
| 261 | + "Julia" => {"Tulip Admiral van Eijck" => 1, "Tulip Semper Augustus" => 2}, |
| 262 | + "Luuk" => {"Tulip Admiral van Eijck" => 1}, |
| 263 | + "Ruben" => {"Tulip Semper Augustus" => 9}, |
| 264 | + "Max" => {"Tulip Admiral van Eijck" => 3}, |
| 265 | + "Martijn" => {"Tulip Admiral van Eijck" => 1}}> |
| 266 | +``` |
| 267 | + |
| 268 | +Or use blocks of transaction classes/structs (with keyword arguments): |
| 269 | + |
| 270 | + |
| 271 | +``` ruby |
| 272 | +ledger = Ledger.new |
| 273 | + |
| 274 | +ledger.write( Block.new( Tx.new( from: "Keukenhof†", to: "Vincent", qty: 11, name: "Tulip Admiral van Eijck" ), |
| 275 | + Tx.new( from: "Vincent", to: "Anne", qty: 3, name: "Tulip Admiral van Eijck" ), |
| 276 | + Tx.new( from: "Anne", to: "Julia", qty: 2, name: "Tulip Admiral van Eijck" ), |
| 277 | + Tx.new( from: "Julia", to: "Luuk", qty: 1, name: "Tulip Admiral van Eijck" )), |
| 278 | + Block.new( Tx.new( from: "Dutchgrown†", to: "Ruben", qty: 11, name: "Tulip Semper Augustus" ), |
| 279 | + Tx.new( from: "Vincent", to: "Max", qty: 3, name: "Tulip Admiral van Eijck" ), |
| 280 | + Tx.new( from: "Ruben", to: "Julia", qty: 2, name: "Tulip Semper Augustus" ), |
| 281 | + Tx.new( from: "Anne", to: "Martijn", qty: 1, name: "Tulip Admiral van Eijck" ))) |
| 282 | + |
| 283 | +pp ledger |
| 284 | +``` |
| 285 | + |
| 286 | +And so on and on. |
| 287 | + |
| 288 | + |
| 289 | + |
| 290 | + |
| 291 | +## Ledger Lite in the Real World |
| 292 | + |
| 293 | +- [**centralbank**](https://github.com/openblockchains/centralbank) - command line tool (and core library) - print your own money / cryptocurrency; run your own federated central bank nodes on the blockchain peer-to-peer over HTTP; revolutionize the world one block at a time |
| 294 | +- [**tulipmania**](https://github.com/openblockchains/tulipmania) - command line tool (and core library) - tulips on the blockchain; learn by example from the real world (anno 1637) - buy! sell! hodl! enjoy the beauty of admiral of admirals, semper augustus, and more; run your own hyper ledger tulip exchange nodes on the blockchain peer-to-peer over HTTP; revolutionize the world one block at a time |
| 295 | +- [**shilling**](https://github.com/bitshilling/bitshilling.tools) - command line tool (and core library) - shilling (or schilling) on the blockchain! rock-solid alpine dollar from austria; print (mine) your own shillings; run your own federated shilling central bank nodes w/ public distributed (hyper) ledger book on the blockchain peer-to-peer over HTTP; revolutionize the world one block at a time |
| 296 | +- You? Add your tool / service |
| 297 | + |
| 298 | + |
| 299 | + |
| 300 | +## References |
| 301 | + |
| 302 | +[**Programming Cryptocurrencies and Blockchains (in Ruby)**](http://yukimotopress.github.io/blockchains) by Gerald Bauer et al, 2018, Yuki & Moto Press |
| 303 | + |
| 304 | + |
| 305 | +## License |
| 306 | + |
| 307 | + |
| 308 | + |
| 309 | +The `ledger-lite` scripts are dedicated to the public domain. |
| 310 | +Use it as you please with no restrictions whatsoever. |
0 commit comments