diff --git a/mining_test.go b/mining_test.go new file mode 100644 index 0000000..a2502ea --- /dev/null +++ b/mining_test.go @@ -0,0 +1,346 @@ +package main + +import ( + "fmt" + "math" + "math/big" + "encoding/hex" + "github.com/obscuren/sha3" + "testing" +) + +//For use in benchmarking +const tree_depth = 5 +const tape_width = 32 //int(math.Pow(2,tree_depth)) < would be nice, but go won't recognize as const +const tape_depth = 500 + +const Report_Zeros = true + +//Number of operations that are drawn from to form the tape and the tree +const num_ops = 7 +//This is the number of times the PoW algorithm is run +const sample_size = 100 + +func Bytes2Hex(d []byte) string { + return hex.EncodeToString(d) +} + +func Hex2Bytes(str string) []byte { + h, _ := hex.DecodeString(str) + return h +} + +func plus(z *big.Int, x *big.Int, y *big.Int) *big.Int { + var lim big.Int + lim.Exp(big.NewInt(2),big.NewInt(256), big.NewInt(0)) + z.Add(x,y) + return z.Mod(z,&lim) + +} + +func times(z *big.Int, x *big.Int, y *big.Int) *big.Int { + var lim, x1, y1, z1 big.Int + lim.Exp(big.NewInt(2),big.NewInt(256), big.NewInt(0)) + x1.Set(x) + y1.Set(y) + z.Mul(x,y) + z.Mod(z,&lim) + z1 = *big.NewInt(0) + if Report_Zeros { + if (z.Cmp(&z1) == 0 && x1.Cmp(&z1) != 0 && y1.Cmp(&z1) != 0) { + fmt.Printf("-------getting zero---------\n") + fmt.Println(Bytes2Hex(x1.Bytes())) + fmt.Println(Bytes2Hex(y1.Bytes())) + fmt.Println(Bytes2Hex(z.Bytes())) + } + } + return z +} + +func mod(z *big.Int, x *big.Int, y *big.Int) *big.Int { + + if (x.Cmp(big.NewInt(0)) == 0 || y.Cmp(big.NewInt(0)) == 0) { + return big.NewInt(0) + } + if x.Cmp(y) == -1 { //if x < y + z.Mod(y,x) + } else if x.Cmp(y) == 1 { //if x > y + z.Mod(x,y) + } + return z +} + +func xor(z *big.Int, x *big.Int, y *big.Int) *big.Int {return z.Xor(x,y)} +func and(z *big.Int, x *big.Int, y *big.Int) *big.Int {return z.And(x,y)} +func or(z *big.Int, x *big.Int, y *big.Int) *big.Int {return z.Or(x,y)} + +func rshift(z *big.Int, x *big.Int, y *big.Int) *big.Int { + var lim big.Int + lim.Exp(big.NewInt(2),big.NewInt(256), big.NewInt(0)) + z.Rsh(x,7) + return z.Mod(z,&lim) +} + + +func GetOp(i int) func(z *big.Int, x *big.Int, y *big.Int) *big.Int{ + switch i { + case 0: + return plus + case 1: + return rshift + case 2: + return mod + case 3: + return xor + case 4: + return and + case 5: + return or + case 6: + return times + } + return plus +} + +type Tapelink struct { + I int64 + J int64 + op func(z *big.Int, x *big.Int, y *big.Int) *big.Int +} + +func Sha3Bin(data []byte) []byte { + d := sha3.NewKeccak256() + d.Write(data) + return d.Sum(nil) +} + +func BenchmarkSha3Bin(b *testing.B){ + for i:= 0; i < b.N; i++ { + Sha3Bin([]byte(string(i))) + } +} + +//generates a tape of operations that is w*d long +func gen_tape(seed int64, w int, d int) []Tapelink { + var X = big.NewInt(0) + var Y = big.NewInt(int64(math.Pow(2,32))) + var M = big.NewInt(0) + var T []Tapelink + for i := 0; i < w*d; i++{ + // add empty link to tape + T = append(T, *new(Tapelink)) + + // generate new entropy as needed + if (int64(X.Cmp(Y)) == -1) {X = X.SetBytes(Sha3Bin([]byte(string(seed + int64(i)))))} + + // Pick random index I + T[i].I = M.Mod(X,big.NewInt(int64(w))).Int64() + M = big.NewInt(int64(w)) + X = X.Div(X,M) + + // Pick random index J + mm := big.NewInt(M.Mod(X,big.NewInt(int64(w-1))).Int64() + int64(1) + T[i].I) + T[i].J = M.Mod(mm, big.NewInt(int64(w))).Int64() + M = big.NewInt(int64(w-1)) + X = X.Div(X,M) + + // Pick random operation + T[i].op = GetOp(int(M.Mod(X, big.NewInt(int64(num_ops))).Int64())) + M = big.NewInt(int64(num_ops)) + X = X.Div(X,M) + } + return T +} + +func BenchmarkGen_tape(b *testing.B){ + var X big.Int + var s int64 + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + X.SetBytes(Sha3Bin([]byte(string(i)))) + s = X.Int64() + b.StartTimer() + gen_tape(s, tape_width, tape_depth) + } + +} + +func gen_inputs(seed int64, w int) []big.Int { + var A []big.Int + X := big.NewInt(0) + for i := 0; i < w; i++ { + var B big.Int + A = append(A,B) + if (i % 256 == 0) { + A[i].Set(X.SetBytes(Sha3Bin([]byte(string(seed + int64(i)))))) + } else { + A[i].Set(X.Lsh(X, 1)) + } + } + return A +} + +func BenchmarkGen_inputs(b *testing.B){ + var X big.Int + var s int64 + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + X.SetBytes(Sha3Bin([]byte(string(i)))) + s = X.Int64() + b.StartTimer() + gen_inputs(s, tape_width) + } +} + +//this changes the inputs as it goes through a tape with d links +func run_tape(tape []Tapelink, inputs []big.Int, d int) { + var X *big.Int + X = big.NewInt(0) + for i := 0; i < d; i++ { + X = tape[i].op(X, &inputs[tape[i].I], &inputs[tape[i].J]) + inputs[tape[i].I].Set(X) + } +} + +func BenchmarkRun_tape(b *testing.B){ + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + T := gen_tape(int64(i), tape_width, tape_depth) + I := gen_inputs(int64(i), tape_width) + b.StartTimer() + run_tape(T,I,tape_width*tape_depth) + } + +} + + +//returns a 2^d - 1 length tape of operations (no I or J's in the Tapelinks) +func gen_tree(seed int64, d int) []Tapelink { + M := big.NewInt(0) // dummy variable + X := big.NewInt(0) // entropy variable + Y := big.NewInt(int64(math.Pow(2,32))) // entropy buffer size + var T []Tapelink // the tree will be stored here + + for i := 0; i < int(math.Pow(2, float64(d))) - 1; i++{ + + T = append(T, *new(Tapelink)) + + //giving it more entropy, if X < 2^32 + if (X.Cmp(Y) == -1) {X = X.SetBytes(Sha3Bin([]byte(string(seed + int64(i)))))} + + //filling the tape with random ops + T[i].op = GetOp(int(M.Mod(X, big.NewInt(num_ops)).Int64())) + M = big.NewInt(num_ops) + X = X.Div(X,M) + } + return T +} + +func BenchmarkGen_tree(b *testing.B) { + var X big.Int + var s int64 + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + X.SetBytes(Sha3Bin([]byte(string(i)))) + s = X.Int64() + b.StartTimer() + gen_tree(s, tree_depth) + } +} + +//there should be 2^d inputs and 2^d - 1 links in the tape. not complying is an unhandled exception +func run_tree(inputs []big.Int, tree []Tapelink, d int) *big.Int { + X := big.NewInt(0) + counter := 0 + for j := 0; j < d; j++ { + for i := 0; i < int(math.Pow(2,float64(d - j - 1))); i++ { + X = tree[counter].op(X, &inputs[2*i], &inputs[2*i + 1]) + inputs[i].Set(X) + counter += 1 + } + } + + return &inputs[0] +} + +func BenchmarkRun_tree(b *testing.B) { + var X big.Int + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + var inputs []big.Int + for j := 0; j < tape_width; j++ { + X.SetBytes(Sha3Bin([]byte(string(j + i*tape_width)))) + inputs = append(inputs, X) + } + tree := gen_tree(X.Int64(), tree_depth) + b.StartTimer() + run_tree(inputs, tree, tree_depth) + } +} + +func sha_cap(s []string, n int) []byte{ + var feed string + feed = "" + for i := 0; i < n; i++ { + feed += s[i] + } + return Sha3Bin([]byte(feed)) +} + + +//benchmarked with only one string in the slice, s +func BenchmarkSha_cap(b *testing.B){ + var X big.Int + var s []string + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + X.SetBytes(Sha3Bin([]byte(string(i)))) + s = append(s, X.String()) + b.StartTimer() + sha_cap(s,1) + } +} + + + +func main(){ + var seed int64 + + var sample []big.Int + for i := 0; i < sample_size; i++ { + + if i%5 == 0 { + fmt.Printf("i: %d\n",i) + } + + seed = int64(i) + T := gen_tape(seed, tape_width, tape_depth) + I := gen_inputs(seed, tape_width) + run_tape(T,I, tape_depth*tape_width) + T = gen_tree(seed, tree_depth) + var output big.Int = *run_tree(I,T,tree_depth) + var blockhashes []string + blockhashes = append(blockhashes, output.String()) + H := sha_cap(blockhashes,1) + + output.SetBytes(H) + sample = append(sample, output) + } + + var collisions int = 0 + for i := 0; i < sample_size; i++ { + for j:=0; j < i; j++ { + if(sample[i].Cmp(&sample[j]) == 0) { + collisions += 1 + } + } + } + fmt.Printf("Collisions: %d out of %d\n", (1+int(math.Pow(float64(1+8*collisions),0.5)))/2 - 1, sample_size) +} +