|
package main |
|
|
|
import ( |
|
"crypto/sha1" |
|
"encoding/hex" |
|
"fmt" |
|
"io" |
|
"io/ioutil" |
|
"log" |
|
"os" |
|
"os/exec" |
|
"strconv" |
|
) |
|
|
|
func main() { |
|
difficulty, tree, parent, timestamp := os.Args[1], os.Args[2], os.Args[3], os.Args[4] |
|
body := fmt.Sprintf(`tree %s |
|
parent %s |
|
author CTF user <[email protected]> %s +0000 |
|
committer CTF user <[email protected]> %s +0000 |
|
|
|
Mined a Gitcoin! `, tree, parent, timestamp, timestamp) |
|
|
|
result := findSha1(body, difficulty) |
|
if result.err != nil { |
|
fmt.Println(result.err) |
|
return |
|
} |
|
writeGitObject(result.msg) |
|
fmt.Printf(result.sha1) |
|
} |
|
|
|
type result struct { |
|
sha1 string |
|
msg string |
|
err error |
|
} |
|
|
|
func findSha1(body string, difficulty string) result { |
|
// fmt.Println(difficulty) |
|
|
|
messages := produce(body) |
|
outcome := make(chan result) |
|
|
|
const numWorkers = 10 |
|
for i := 0; i < numWorkers; i++ { |
|
go consume(messages, difficulty, outcome) |
|
} |
|
|
|
return <-outcome |
|
} |
|
|
|
func consume(messages <-chan string, difficulty string, c chan<- result) { |
|
for msg := range messages { |
|
header := fmt.Sprintf("commit %d\000", len(msg)) |
|
content := header + msg |
|
|
|
hasher := sha1.New() |
|
io.WriteString(hasher, content) |
|
sha1 := hex.EncodeToString(hasher.Sum(nil)) |
|
|
|
if sha1 < difficulty { |
|
c <- result{sha1, msg, nil} |
|
} |
|
} |
|
} |
|
|
|
func writeGitObject(msg string) { |
|
os.Chdir("level1") |
|
cmd := exec.Command("git", "hash-object", "-t", "commit", "-w", "--stdin") |
|
stdin, err := cmd.StdinPipe() |
|
if err != nil { |
|
log.Fatalf("Problem getting stdin: %s", err) |
|
} |
|
stderr, err := cmd.StderrPipe() |
|
if err != nil { |
|
log.Fatalf("Problem getting stderr: %s", err) |
|
} |
|
stderrC := make(chan string) |
|
go func() { |
|
out, err := ioutil.ReadAll(stderr) |
|
if err == nil { |
|
stderrC <- string(out) |
|
} else { |
|
stderrC <- err.Error() |
|
} |
|
}() |
|
err = cmd.Start() |
|
if err != nil { |
|
log.Fatalf("Problem starting the command: %s", err) |
|
} |
|
n, err := io.WriteString(stdin, msg) |
|
if n != len(msg) { |
|
log.Fatalf("expected to write %d but wrote %d", len(msg), n) |
|
} |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
if err = stdin.Close(); err != nil { |
|
log.Fatal(err) |
|
} |
|
err = cmd.Wait() |
|
if err != nil { |
|
log.Fatalf("%s\n\n%s\n", err, <-stderrC) |
|
} |
|
} |
|
|
|
func produce(body string) chan string { |
|
yield := make(chan string) |
|
count := 0 |
|
go func() { |
|
for { |
|
yield <- body + strconv.Itoa(count) |
|
count++ |
|
} |
|
}() |
|
return yield |
|
} |