Created
May 31, 2021 19:37
-
-
Save fserb/aee1beab9a0ca307ee3f6374c2e07543 to your computer and use it in GitHub Desktop.
A blockchain implementation on top of git in 50 lines of Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import subprocess, sys, os, tempfile, re | |
""" | |
How to use this: | |
1. create a git repo | |
2. commit something to it | |
3. run ./chainy.py <number of zeroes> | |
where <number of zeroes> is how many zeroes you want the git commit hash to | |
have. You can try with 2 or 3 for fast results. Bigger numbers are safer, but | |
will take longer. | |
Repeat step 2, 3 as many times as you want and you will end up with a git repo | |
where all commits have the correct number of zeroes. This gives you the exact | |
same guarantee of Bitcoin: that if you someone wants to fake your history, they | |
would have to redo all processing work that chainy put on finding the | |
zero-ending hashes. I.e., the repo itself has proof of work. | |
It works by adding a "Chainy: <padnumber>" to the latest git commit. Where | |
<padnumber> is a number that makes the git commit's SHA-1 end with the correct | |
number of zeroes. Finding this number, i.e., "mining it", is what this script | |
does. In an extremely inneficient way. | |
""" | |
def git(*cmd): | |
with subprocess.Popen(["git", *cmd], | |
env=os.environ, stdout=subprocess.PIPE) as p: | |
p.wait() | |
return p.stdout.read().decode('utf-8').strip() | |
def main(args): | |
if len(args) == 1: | |
print("Pass number of zeroes") | |
return 1 | |
zeroes = int(args[1]) | |
print("Setting %d zeroes" % (zeroes,)) | |
if not git("rev-parse", "--git-dir"): | |
print("Must be run inside a git repo") | |
return 2 | |
clean_tree = len(git("diff-index", "--name-only", "HEAD", "--")) == 0 | |
if not clean_tree: | |
print("Uncommmited changes") | |
return 3 | |
while True: | |
head = git("rev-parse", "HEAD") | |
if head[-zeroes:] == "0" * zeroes: | |
print(head) | |
break | |
msg = git("log", "-1", "--pretty=%B").split('\n') | |
last_line = msg[-1] | |
c = re.findall("Chainy: (\d+)$", last_line) | |
if not c: | |
c = 0 | |
else: | |
msg = msg[:-2] | |
c = int(c[0]) + 1 | |
msg.append("") | |
msg.append("Chainy: %d" % (c,)) | |
if c % 100 == 0: print(head, c) | |
with tempfile.NamedTemporaryFile('wt') as tmp: | |
tmp.write('\n'.join(msg)) | |
tmp.seek(0) | |
git("commit", "--amend", "--file", tmp.name) | |
return 0 | |
if __name__ == "__main__": | |
sys.exit(main(sys.argv)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment