Commit 31b34be
Changed files (4)
config.py
@@ -0,0 +1,13 @@
+import os # import path, getcwd, urandom
+
+USERNAME = "user-zueglyh2"
+EMAIL = USERNAME+"@gitco.in"
+#REMOTE_URI = "lvl1-k61vtf8e@stripe-ctf.com:level1"
+REMOTE_URI = "lvl1-k61vtf8e@stripe-ctf.com:current-round"
+REMOTE_BRANCH = 'origin/master'
+REPO_DIR = os.path.join(os.getcwd(), '.cache-'+os.urandom(4).encode('hex'))
+LEDGER_FILENAME = "LEDGER.txt"
+LEDGER_PATH = os.path.join(REPO_DIR,LEDGER_FILENAME)
+DIFFICULTY_PATH = os.path.join(REPO_DIR,"difficulty.txt")
+MESSAGE_LENGTH = 16
+GIT_POLL_INTERVAL = 45
gitcoin.py
@@ -1,94 +1,197 @@
-#!/usr/bin/python
-
from __future__ import print_function
import pygit2
# brew install libgit2
# pip install pygit2
-from tempfile import mkstemp
-from os import remove, close, path, stat, chmod
-from shutil import move
-from hashlib import sha1
-from random import randint
-from time import time
-import sys # print to stderr
-
-def update_ledger(ledger_path):
+import tempfile # import mkstemp
+import os # import remove, close, path, stat, chmod, urandom, mkdir, devnull
+import shutil # import move, rmtree
+import hashlib # import sha1
+import time # import time
+import subprocess # import call, STDOUT
+import sys # import stderr
+from config import * # global variables
+
+def init_repo(repo_dir, remote_uri):
+ """initialize (destructive) a repo into repo_dir from remote_uri"""
+ shutil.rmtree(repo_dir, ignore_errors=True)
+ os.mkdir(repo_dir)
+ # pygit2 doesn't like the way stripe's git repo is setup
+ # I don't care enough to actually figure out why...
+ # any feedback on this would be appreciated :)
+ subprocess.call(["git", "clone", remote_uri, repo_dir],
+ stdout=open(os.devnull,'w'),
+ stderr=subprocess.STDOUT)
+ print("gitchain init complete", file=sys.stderr)
+ return pygit2.init_repository(repo_dir)
+
+def update_ledger(ledger_path, username):
+ """add one gitcoin to the username in the ledger file at ledger_path"""
coin_added = False
ledger = open(ledger_path, "r")
- fh, abs_path = mkstemp()
+ fh, abs_path = tempfile.mkstemp()
new_ledger = open(abs_path, "w")
-
for line in ledger:
if username in line:
new_ledger.write(username+": "+str(int(line.split(": ")[1])+1)+"\n")
coin_added = True
else:
new_ledger.write(line)
-
if not coin_added:
new_ledger.write(username+": 1\n")
-
new_ledger.close()
- close(fh)
+ os.close(fh)
ledger.close()
- remove(ledger_path)
- move(abs_path, ledger_path)
- chmod(ledger_path, 0644)
-
-def add_to_repo(repo, repo_path, filename):
- "add filename to repository located at path and return the tree object id"
- blob = repo.create_blob_fromworkdir(filename)
+ os.remove(ledger_path)
+ shutil.move(abs_path, ledger_path)
+ os.chmod(ledger_path, 0644)
+
+def fastforward_repo(repo, repo_dir, remote_branch):
+ """reset repository at repo_dir and return the resulting pygit2 repo object"""
+ # see previous about fetching issues with pygit2
+ subprocess.call(["git", "fetch", "--all"],
+ cwd=repo_dir,
+ stdout=open(os.devnull,'w'),
+ stderr=subprocess.STDOUT)
+ fastforward = False
+ # get the oid of the remote branch, if != current head fastforward
+ branch = repo.lookup_branch(remote_branch, pygit2.GIT_BRANCH_REMOTE)
+ if branch.target.hex != repo.head.target.hex:
+ print("fast forwarding gitchain", file=sys.stderr)
+ # merge to remote, if conflicts occur it is because a genesis event
+ try: merge_result = repo.merge(repo.get(branch.target.hex).oid)
+ except pygit2.GitError:
+ print("gitchain had a genesis event: fetching!", file=sys.stderr)
+ return init_repo(repo_dir, repo.remotes[0].url), True
+ if merge_result.is_fastforward:
+ repo.reset(merge_result.fastforward_oid, pygit2.GIT_RESET_HARD)
+ if (branch.target.hex
+ == merge_result.fastforward_oid.hex
+ == repo.head.target.hex):
+ fastforward = True
+ else:
+ raise Exception("git reset failed!")
+ else:
+ raise Exception("git merge failed!")
+ else:
+ print("no gitchian updates...", file=sys.stderr)
+ return repo, fastforward
+
+def tree_add_to_repo(repo, repo_dir, filename):
+ """
+ add filename to repository and return the repo and the tree oid
+ unfortunately this broke on commit, so instead see index_add_to_repo
+ """
+ oid = repo.create_blob_fromworkdir(filename)
treebuilder = repo.TreeBuilder()
- treebuilder.insert(filename, blob, stat(path.join(repo_path,filename)).st_mode)
+ treebuilder.insert(filename,
+ oid,
+ os.stat(os.path.join(repo_dir,filename)).st_mode)
tree_oid = treebuilder.write()
- return tree_oid
-
-def reset_repo(repo):
- pass
-
-def githash(data):
- s = sha1()
- s.update("commit %u\0" % len(data))
- s.update(data)
- return s.hexdigest()
-
-def mine_gitcoin(repo, repo_path):
- difficulty_path = path.join(repo_path,"difficulty.txt")
- difficulty = open(difficulty_path,"r").read().split()[0]
- sig = pygit2.Signature(name='gitcoinminer', email='git@coin.co', time=time())
- tree = add_to_repo(repo, repo_path, ledger_filename)
- hex_head = [] if repo.is_empty else [repo.head.target.hex]
-
- count = randint(0, sys.maxint/2)
- start = time()
+ return repo, tree_oid
+
+def index_add_to_repo(repo, repo_dir, filename):
+ """add filename to repository and return the repo and the tree oid"""
+ repo.index.read()
+ repo.index.add(filename)
+ return repo, repo.index.write_tree()
+
+def gen_commit_context(repo, tree, username, email, time, msg_len):
+ """return the sha1 context lacking only the message portion of the commit"""
+ head = repo.head.target.hex
+ body = ("tree "+tree+"\n"
+ "parent "+head+"\n"
+ "author "+username+" <"+email+"> "+str(int(time))+" +0000\n"
+ "committer "+username+" <"+email+"> "+str(int(time))+" +0000\n\n")
+ s = hashlib.sha1()
+ s.update("commit %u\0" % (len(body)+msg_len))
+ s.update(body)
+ return s
+
+def gen_commit(repo, tree, username, email, time, message):
+ """ useless function that creates the signature and commit """
+ sig = pygit2.Signature(name=username, email=email, time=time)
+ head = repo.head.target.hex
+ return repo.create_commit(repo.head.name, sig, sig, message, tree, [head])
+
+def proof_of_work(s, msg_len, difficulty, timeout):
+ # TODO: generalize and zmq this work out
+ """return message, digest, success"""
+ freq = 168384
+ start = time.time()
+ worker_time = time.time()
+ count = 2
while True:
-
- nonce = str(count) #sha1(str(count)).hexdigest()
- c = repo.create_commit(repo.head.name, sig, sig, nonce, tree, hex_head)
-
count += 1
- if c.hex < difficulty: break
- if (count % 1024) == 1:
- end = time()
- print("%0.3f Kh/s ... %d" % (1/(end-start), count), end='\n', file=sys.stderr)
- start = time()
- break
-
- print (c.hex)
-
-username = "user-zueglyh2"
-repo_path = "/Users/bryon/r-bryfry/level1/"
-
-ledger_filename = "LEDGER.txt"
-ledger_path = path.join(repo_path,ledger_filename)
-
-repo = pygit2.Repository(repo_path)
-reset_repo(repo)
-update_ledger(ledger_path)
-mine_gitcoin(repo, repo_path)
-
-
-#write to disk
-#repo.index.read()
-#repo.index.add(filename)
-#repo.index.write()
+ message = os.urandom(msg_len/2).encode('hex')
+ tmp_s = s.copy()
+ tmp_s.update(message)
+ if tmp_s.hexdigest() < difficulty:
+ return message, tmp_s.hexdigest(), True
+ if (count % freq) == 1:
+ hashrate = (((1/(time.time()-start))*freq)/1024)
+ print("~ %0.3f Kh/s" % hashrate, end='\n', file=sys.stderr)
+ start = time.time()
+ if (time.time()-worker_time) > timeout:
+ return "", "", False
+
+if __name__ == "__main__":
+ repo = init_repo(repo_dir=REPO_DIR, remote_uri=REMOTE_URI)
+ commit_time = time.time()
+
+ # add gitcoin to the repository and start commit sha1
+ difficulty = open(DIFFICULTY_PATH,"r").read().split()[0]
+ update_ledger(ledger_path=LEDGER_PATH, username=USERNAME)
+ repo, tree = index_add_to_repo(repo=repo,
+ repo_dir=REPO_DIR,
+ filename=LEDGER_FILENAME)
+ s = gen_commit_context(repo=repo,
+ tree=tree.hex,
+ username=USERNAME,
+ email=EMAIL,
+ time=commit_time,
+ msg_len=MESSAGE_LENGTH)
+
+ while True:
+ message, digest, success = proof_of_work(s=s,
+ msg_len=MESSAGE_LENGTH,
+ difficulty=difficulty,
+ timeout=GIT_POLL_INTERVAL)
+ repo, fastforward = fastforward_repo(repo=repo,
+ repo_dir=REPO_DIR,
+ remote_branch=REMOTE_BRANCH)
+ if success and not fastforward:
+ print("mined gitcoin! commit-hash:[%s]" % (message, digest),
+ file=sys.stderr)
+ elif fastforward:
+ # re-add our gitcoin to the reset --hard'd repository
+ difficulty = open(DIFFICULTY_PATH,"r").read().split()[0]
+ update_ledger(ledger_path=LEDGER_PATH, username=USERNAME)
+ repo, tree = index_add_to_repo(repo=repo,
+ repo_dir=REPO_DIR,
+ filename=LEDGER_FILENAME)
+ s = gen_commit_context(repo=repo,
+ tree=tree.hex,
+ username=USERNAME,
+ email=EMAIL,
+ time=commit_time,
+ msg_len=MESSAGE_LENGTH)
+
+ c = gen_commit(repo=repo,
+ tree=tree,
+ username=USERNAME,
+ email=EMAIL,
+ time=commit_time,
+ message=message)
+
+ # make sure we really have a winner
+ s.update(message)
+ if c.hex != s.hexdigest():
+ raise Exception("commit hash mismatch!")
+
+ # again, making up for pygit2's shortcomings...
+ subprocess.call(["git", "push", "origin", "master"],
+ cwd=REPO_DIR,
+ stdout=open(os.devnull,'w'),
+ stderr=subprocess.STDOUT)
+ # maybe dont need separate dirs?
+ shutil.rmtree(REPO_DIR, ignore_errors=True)
my-miner
@@ -1,97 +0,0 @@
-#!/bin/bash
-
-set -eu
-
-if [ "$#" != 2 ]; then
- echo >&2 "Usage: $0 <clone_url> <public_username>
-
-A VERY SLOW mining implementation. This should give you an idea of
-where to start, but it probably won't successfully mine you any
-Gitcoins.
-
-Arguments:
-
-<clone_url> is the string you'd pass to git clone (i.e.
- something of the form username@hostname:path)
-
-<public_username> is the public username provided to you in
- the CTF web interface."
- exit 1
-fi
-
-export clone_spec=$1
-export public_username=$2
-
-prepare_index() {
- perl -i -pe 's/($ENV{public_username}: )(\d+)/$1 . ($2+1)/e' LEDGER.txt
- grep -q "$public_username" LEDGER.txt || echo "$public_username: 1" >> LEDGER.txt
-
- git add LEDGER.txt
-}
-
-solve() {
- # Brute force until you find something that's lexicographically
- # small than $difficulty.
- difficulty=$(cat difficulty.txt)
-
- # Create a Git tree object reflecting our current working
- # directory
- tree=$(git write-tree)
- parent=$(git rev-parse HEAD)
- timestamp=$(date +%s)
- body="tree $tree
-parent $parent
-author CTF user <me@example.com> $timestamp +0000
-committer CTF user <me@example.com> $timestamp +0000
-
-Give me a Gitcoin
-
-"
-
- # See http://git-scm.com/book/en/Git-Internals-Git-Objects for
- # details on Git objects.
- #time sha1=$(echo "${body}${counter}" | git hash-object -t commit --stdin)
- #sha1=$(git hash-object -t commit --stdin <<< "${body}${counter}")
- coin=$(python ../miner/gitcoin.py <<< "$body")
- #git hash-object -t commit --stdin -w <<< "$coin" > /dev/null
- sha1=$(git hash-object -t commit --stdin <<< "$coin")
- echo "SHA1 $sha1"
- (printf "commit %s\0" $(wc -c <<< "$body"); cat <<<"$body") | shasum
- (printf "commit %s\0" $(wc -c <<< "$coin"); cat <<<"$coin") | shasum
- #git reset --hard "$sha1"
- break
-}
-
-reset() {
- git fetch origin master >/dev/null 2>/dev/null
- git reset --hard origin/master >/dev/null
- git stash >/dev/null
- git pull
- echo "Current difficulty:"
- cat difficulty.txt
-}
-
-# Set up repo
-local_path=./${clone_spec##*:}
-
-if [ -d "$local_path" ]; then
- echo "Using existing repository at $local_path"
- cd "$local_path"
-else
- echo "Cloning repository to $local_path"
- git clone "$clone_spec" "$local_path"
- cd "$local_path"
-fi
-
-while true; do
- reset
- prepare_index
- solve
- if git push origin master; then
- echo "Success :)"
- break
- else
- echo "Starting over :("
- reset
- fi
-done
old.py
@@ -1,31 +0,0 @@
-#!/usr/bin/python
-from __future__ import print_function
-import sys
-import time
-#from random import random
-from hashlib import sha1
-
-def githash(data):
- s = sha1()
- s.update("commit %u\0" % len(data))
- s.update(data)
- return s.hexdigest()
-
-if __name__ == "__main__":
- counter = 0
- body = sys.stdin.read()
- difficulty = open("./difficulty.txt","r").read().split()[0]
- while True:
- start = time.time()
- #print(counter%1000, file=sys.stderr)
- #nonce = sha1(str(random())).hexdigest()
- counter += 1
- commit = body+str(counter)
- #if githash(body+str(counter)) < difficulty:
- print(commit)
- print("bodyhash "+githash(body), file=sys.stderr)
- print("githash "+githash(commit), file=sys.stderr)
- break
- end = time.time()
- print("%g Kh/s ... %d " % (1/(end-start)/1024, counter), end='', file=sys.stderr)
-