16 ms·
Show HN: Legion, an as-simple-as-possible blockchain server written in Haskell
- cocktailpeanuts 9y agoThanks for sharing, the code looks very elegant. This alone makes me want to sit down and learn Haskell.
- artursapek 9y agoHaskell is known for that
- aviaviavi 9y agoThanks, you definitely should! It's very fun to write code in.
- jstanley 9y agoI had a brief look through, and it looks like there's no proof-of-work in the mining? What stops somebody from rewriting history and passing it off as legitimate?
- empath75 9y agoNothing. It's a minimal blockchain.
- aviaviavi 9y agoCorrect, the goal for this was as minimal as possible. However, if I (or anyone wanting to contribute) can come up with a very compact way to implement that, we could definitely add it!
- 18nleung 9y agoTo implement rudimentary proof of work, you could add a nonce field and then force every block hash to start with a certain number of zeroes - I'll see what I can do and submit a pull!
- aviaviavi 9y agoAwesome, please do!
- terhechte 9y agoA very simple proof of work is adding a token to the data before hashing: sha256(token + data + previous hash) i.e. (for simplicity, previous hash = "") sha256("1" + "example" + PreviousHash) -> ee73735c2355bc4d63319a6638e356ef42d0d56746317c99f52d3e7ac71cbf52 sha256("2" + "example" + PreviousHash) -> 6236cd42286c74a1683eaf394b3b2f40a3a52479e1a3d7e732175fcdfa49b931 ... sha256("10" + "example" + PreviousHash) -> 346fea90403e733fd6e925920eb10be06171b4f77f7cc8c4ef214a12f04d79c1 And only accepting hashes as valid where the first two characters form "34". So the last example (token 10) would be valid. This requires then to do multiple hashing operations until a valid hash emerges, thus the proof of work.
- aviaviavi 9y agoThanks for simple explanation! Maybe between myself and perhaps 18nleung we can get a simple version of this in there
- vog 9y agoNote that "the first X characters" is kind of misleading, because it is usually more fine grained. It is better (and IMHO even simpler!) to think of the hash as a large integer, not a string. Then you want that integer to be smaller than a certain value. With strings, you can only say: "00123" shall start with "00", while wih integers, you would say: 00123 shall be smaller than 300. (To be that fine grained with strings, you would either need multiple prefixes ("000", "001", "002") regexes ("00[012]"), both more hassle than a simple less-than operation.)
- runeks 9y agoA blockchain without a consensus algorithm is just a linked list.
- hestefisk 9y ago... had this been written in a language less esoteric than Haskell, it would've been even better for the rest of us :)
- codnee 9y agoAlmost exactly my thought every time I see something written in JS.
- aviaviavi 9y agoI suggest you take a look at https://github.com/lhartikk/naivechain https://github.com/lhartikk/naivechain, the original version of this work, written in JS
- flavio81 9y agoI second the recommendation for Naivechain. It is really easy to understand. For me, reading the Javascript code of Naivechain made me understand Blockchains in an easier-to-understand way than all the tutorials that try to explain how to implement a blockchain!!
- solidsnack9000 9y agoI think it's too bad we don't have module interoperability. There's never going to be just one programming language. The situation on the JVM -- where Scala, Java and Kotlin can use one another's modules -- is a positive example. Clang modules and Swift's auto-magic import of them is another.
- aabaker99 9y agoI'm pretty uninformed about libffi but can it help with kind of module interoperability? I've used it to get Ruby to call C functions. I would guess that it could be used for other dynamic languages to call compiled functions such as JS to Haskell like the OP seems to be desiring. Is this true?
- 9y ago
- greglindahl 9y agoOf course there are name collisions everywhere, but, there's a metacomputing/grid computing/distributed OS thingie named "Legion" from the University of Virginia that has a bunch of papers about it in the CS literature. Oh, and we saw off the American Legion threatening to sue us by saying "We're the State of Virginia, go right ahead..."; what's your strategy? :-)
- aviaviavi 9y agoHeh, well if there are actually problems I can definitely change it... But I only meant this to be a fun, small, open source blockchain example (and to learn more Haskell). So I didn't put too much effort into the naming part :)
- jboynyc 9y agoYou mean this project might be summoning daemons that will claim, "Our name is Legion"? http://bible.oremus.org/?ql=363093142 http://bible.oremus.org/?ql=363093142
- greglindahl 9y agoHumorously, the professor who named it thought the quote was from Heinlein. Not much of a Bible-reader. Edit: Apparently I'm not very good at remembering my SciFi, maybe it's this? https://en.wikipedia.org/wiki/My_Name_Is_Legion_(short_story_collection) https://en.wikipedia.org/wiki/My_Name_Is_Legion_(short_story...
- shadytrees 9y agoGreat use of distributed-process and distributed-process-p2p! Being able to access service discovery and peer-to-peer communication as simple Haskell packages is kind of amazing. * https://hackage.haskell.org/package/distributed-process-p2p https://hackage.haskell.org/package/distributed-process-p2p * https://hackage.haskell.org/package/distributed-process https://hackage.haskell.org/package/distributed-process
- aviaviavi 9y agoThose libraries were essential for this project! High quality. Are you a contributor to them? I actually had a slightly tough time finding good examples to work off of for p2p, (the cloud haskell website had the best ones), so hopefully this code can also work as another good example for those packages. To be fair, that's really a complaint of the ecosystem as a whole, not distributed-process-* specifically.
- SkyMarshal 9y agoIn case you're not aware of it, also check out Haskoin, a Bitcoin implementation in Haskell. https://github.com/haskoin/haskoin https://github.com/haskoin/haskoin
- m-j-fox 9y agoNicely done. It reads like Ikea instructions. I didn't really know how blockchain worked until I read this code. If this was written as a teaching tool or a portfolio piece or even an art project, then I say its a big success. One question: getBlockChain :: (SpockState m ~ BlockChainState, MonadIO m, HasSpock m) => m [Block] What does the squiggle mean?
- eutectic 9y agoIt is an equality assertion: https://downloads.haskell.org/~ghc/7.6.1/docs/html/users_guide/equality-constraints.html https://downloads.haskell.org/~ghc/7.6.1/docs/html/users_gui...
- m-j-fox 9y agoThanks, but I still don't get it. So SpockState m equals BlockChainState? So getState's type is m BlockChainState? (BlockChainState chain _ _) <- getState But it has to be anyway so, I guess I'm still confused. Is the squiggle for extra documentation or is it a hint to help the type inference? Why would type inference need help? Does it have to do with some of the extensions that are applied in the source (but appear to be otherwise unused)?
- aviaviavi 9y agoYeah, it's for the compiler, otherwise it can't figure out that a BlockChainState is actually a SpockState m. I'm not really sure how it could otherwise, since the definition is completely decoupled from Spock. It should be noted that it's entirely possible there's a less confusing way to write that type signature. I'm far from an expert in Haskell.
- m-j-fox 9y agoI'm farther from an expert than you. I tried (and failed) to find a less confusing way... One thing I see people doing when they have real gnarly type signature is to concentrate the horror in one place and paint a veneer of simplicity elsewhere: type Get stateType returnType = forall monad. (SpockState monad ~ stateType, MonadIO monad, HasSpock monad) => monad returnType Then elsewhere: getBlockChain :: Get BlockChainState [Block] getLatestBlock :: Get BlockChainState Block This may only help superficially as type errors will still be hard-to-understand.
- KirinDave 9y agoI'm tempted to just make a youtube video going over this code line by line. It's a really great introduction to a non-trivial bit of Haskell.
- aviaviavi 9y agoThat would be awesome! Hopefully over time I (or anyone contributing) can spend a little more time making it as idiomatic as possible and beefing up the docs a little bit. Please let me know if you end up doing that!
- KirinDave 9y agoOkay. And your code is just very clean and approachable and doesn't try to do popular gymnastics. I'll ping you via GitHub.
- tomcam 9y agoPlease do
- seanparsons 9y agoWould it make sense to make the code Literate Haskell?
- aviaviavi 9y agoI'm not familiar with Literate Haskell, what would that involve?
- KirinDave 9y agoIt's just where the majority of the file is a comment instead of a minority.
- piaste 9y agoThank you. Thank you so much. It's an old pet peeve of mine. I cringe inside every time someone starts waxing about "writing your thoughts first, and your code only incidentally" or "it's a completely different paradigm of programming" and other such pieces of useless, discouraging new age fluff.
- eemax 9y agoNeat. Just browsing through the source, is there a reason that: calculateBlockHash (Block i p t b _) = concatMap hashString [show i, p, show t, b] is not calculateBlockHash (Block i p t b _) = hashString $ concat [show i, p, show t, b] i.e. why is a block hash a concatenation of 4 SHA-256 hashes instead of just 1? Is there some security benefit of doing it one way vs. the other? edit: thinking about it a little more, option 2 seems better because in option 1 you can calculate 3/4 of a block hash without knowing the previous block's hash. Maybe that's not a problem in this context though?
- aviaviavi 9y agoYou're correct, it really should be the 2nd way. The current form isn't really a problem per se, mostly just that the generated hashes are really long and make the output harder to read. I'll get that updated, great catch. EDIT: code updated.
- erikb 9y agoCan someone explain to me in short summary why blockchain without crypto currency suddenly is a thing and why anybody would want it?
- quchen 9y agoELI5: A blockchain is an »I told you so« store. Future statements/promises are linked to past ones so you can’t mess one without having to mess with all the others. It’s like a black board you can’t wipe clean again, adding more text (and space to write to) is the only thing possible. For example, you could promise to paint your house red in a blockchain. You later regret that promise and tell your friends you never said that, but since they all have access to all promises made, they have good evidence that what you say is not true.
- jstanley 9y agoExcept without proof-of-work, rewriting all of history is trivial.
- kaoD 9y agoA simple counter-example comes to mind: rewriting history is trivial only if you want no central authority. Even if we lose the "decentralized consensus" aspect with no PoW, it still can be advantageous. E.g.: simply having an authoritative server always responding with the latest block hash. The server is very lightweight (as in 80s microcontroller lightweight) and it can offload the actual blockchain handling to clients.
- erikb 9y agoThanks, very good explanation. So blockchain enables us to move from "our data warehouse is the single source of truth" to "our data warehouse is the single History of truth".
- kaoD 9y agoI'd say more like "this tiny [latest] block is a strong proof of history".
- bernardlunn 9y agoMy takeaway from this was to learn more about Literate Haskell. As somebody points out a Blockchain without a consensus mechanism is simply a linked list ie this is not really about pushing the envelope of Blockchain tech.