function chalang_main() { const word_size = 4294967296, hash_size = 32; const ops = {int4: 0, int1: 3, int2: 4, binary_op: 2, print: 10, finish: 11, //because 'return' is reserved. nop: 12, fail: 13, drop: 20, dup: 21, swap: 22, tuck: 23, rot: 24, ddup: 25, tuckn: 26, pickn: 27, to_r: 30, from_r: 31, r_fetch: 32, hash_op: 40, verify_sig: 41, add: 50, subtract: 51, mul: 52, div: 53, gt: 54, lt: 55, pow: 56, rem: 57, eq: 58, eq2: 59, caseif: 70, caseelse: 71, casethen: 72, bool_flip: 80, bool_and: 81, bool_or: 82, bool_xor: 83, bin_and: 84, bin_or: 85, bin_xor: 86, stack_size: 90, height: 94, gas: 96, ram: 97, many_vars: 100, many_funs: 101, define: 110, fun_end: 111, recurse: 112, call: 113, def: 114, set: 120, fetch: 121, cons: 130, car: 131, empty_list: 132, append: 134, split: 135, reverse: 136, is_list: 137}; function memory(x) { if (JSON.stringify(x) == "[]") { return 1; } else if (Number.isInteger(x)) { return 4; } else if (x[0] == "binary") { return x.length - 1; } else { var y = x.slice(1); return memory(x[0]) + memory(y); } } function underflow_check(d, min_size, op_name) { /*if(!(d.stack)){ console.log("chalang error"); console.log(JSON.stringify(d)); }*/ if (d.stack.length < min_size) { throw(JSON.stringify(["error", "stack underflow", op_name])); } } function exponential(b, a) { if (b == 0) { return 0; } else if (a == 0) { return 1; } var n = 1; while (a > 1) { if ((a % 2) == 0) { b = b*b; a = Math.floor(a / 2); } else { a = a - 1; n = n * b; } } return b * n; } function arithmetic_chalang(op, a, b) { //returns a list to concat with stack. var x; var d = {"stack":[]}; var i = 0; if (op == ops.add) { op_print(d, i, "add op"); x = a + b; } else if (op == ops.subtract) { op_print(d, i, "subtract op"); x = b - a; } else if (op == ops.mul) { op_print(d, i, "mul op"); x = b * a; } else if (op == ops.div) { op_print(d, i, "div op"); x = Math.floor(b / a); } else if (op == ops.pow) { op_print(d, i, "pow op"); x = exponential(b, a); } else if (op == ops.rem) { op_print(d, i, "rem op"); x = b % a; } else if (op == ops.gt) { op_print(d, i, "gt op"); if (b > a) { x = 1; } else { x = 0; } } else if (op == ops.lt) { op_print(d, i, "lt op"); if (b < a) { x = 1; } else { x = 0; } } x = ((x % word_size) + word_size) % word_size; return [x]; } function small_hash(l) { var h = hash(l); return h.slice(0, hash_size); } function split_if(opcode, code) { var a = 0; for (var i = 0; i < code.length; i++) { if ((code[i]) == ops.int4) { i += 4; } else if (code[i] == ops.binary_op) { var h = array_to_int(code.slice(i+1, i+5)); i += (4 + h); } else if ((code[i] == ops.caseif)){ var k = count_till(code, i+1, ops.casethen); i += (k); } else if (opcode == code[i]) { return {"rest": code.slice(i), "code": code.slice(0, i), "n": i}; } } throw("split if error"); } function count_till(code, i, opcode) { for (var j = 0; (j + i) < code.length; j++) { if ((code[i+j]) == ops.int4) { j += 4; } else if (opcode == code[i+j]) { return j; } else if (code[i+j] == ops.binary_op) { var h = array_to_int(code.slice(i+j+1, i+j+5)); j += (4 + h); } else if ((code[i+j] == ops.caseif)){ var k = count_till(code, i+j+1, ops.casethen); j += (k + 1); } } console.log(opcode); throw("count till reached end without finding goal"); } function replace(old_character, new_code, binary) { for (var i = 0; i < binary.length; i++) { if (binary[i] == old_character) { var r2 = replace(old_character, new_code, binary.slice(i+1)); return binary.slice(0,i).concat(new_code).concat(r2); } else if (binary[i] == ops.int4) { i += 4; } else if (binary[i] == ops.binary_op) { var h = array_to_int(binary.slice(i+1, i+5)); i += (4 + h); } } return binary; } var verbose = false; var stack_verbose = false; function op_print(d, i, x) { if (verbose) { console.log(("# ").concat( (i).toString()).concat( " ").concat(x)); } if (stack_verbose) { console.log(JSON.stringify(d.stack)); } } var op_code = {}; op_code[ops.int1] = function(i, code, d){ var int_array = code.slice(i+1, i+2); var new_int = array_to_int(int_array); d.stack = ([new_int]).concat(d.stack); return {i: i+1, d: d, g: 1, s: "int1", r: 1}; }; op_code[ops.int2] = function(i, code, d){ var int_array = code.slice(i+1, i+3); var new_int = array_to_int(int_array); d.stack = ([new_int]).concat(d.stack); return {i: i+2, d: d, g: 1, s: "int2", r: 1}; }; op_code[ops.int4] = function(i, code, d) { var int_array = code.slice(i+1, i+5); var new_int = array_to_int(int_array); d.stack = ([new_int]).concat(d.stack); return {i: i+4, d: d, g: 1, s: "int op", r: 1}; }; op_code[ops.binary_op] = function(i, code, d) { var int_array = code.slice(i+1, i+5); var new_int = array_to_int(int_array); var bin_array = code.slice(i+5, i+5+new_int); var bin1 = (["binary"]).concat(bin_array); d.stack = ([bin1]).concat( d.stack); return {i: i+4+new_int, d: d, g: new_int, s: "bin op", r: 1}; } op_code[ops.caseif] = function(i, code, d) { var b = d.stack[0]; var size_case1 = count_till(code, i + 1, ops.caseelse); if (b == 0) { i += (size_case1 + 1); } d.stack = d.stack.slice(1); return {i: i, d: d, g: 0, s: "if op"}; } op_code[ops.caseelse] = function(i, code, d) { var skipped_size = count_till(code, i + 1, ops.casethen); i += (skipped_size + 0); return {i: i, d: d, g: 0, s: "else op"}; } op_code[ops.casethen] = function(i, code, d) { // do nothing. return {i: i, d: d, g: 0, s: "then op"}; } op_code[ops.call] = function(i, code, d) { //non-optimized function call. var code_hash=btoa(array_to_string(d.stack[0].slice(1))); var definition = d.funs[code_hash]; if (definition == undefined) { console.log("undefined function"); console.log(code_hash); } else { //console.log("function named "); //console.log(code_hash); } var s = definition.length; d.stack = d.stack.slice(1); d = run2(definition, d); //console.log("d after ops call "); //console.log(JSON.stringify(d)); return {i: i, d: d, g: (s + 10), s: "slow call op", r: (s - 1)}; } op_code[ops.def] = function(i, code, d) { var skipped_size = count_till(code, i, ops.fun_end); var definition = code.slice(i+1, i+skipped_size); //console.log("chalang define definition is "); //console.log(definition); //console.log(JSON.stringify(definition)); i += skipped_size; //var hash_array = small_hash(definition); var hash_array = hash(definition); var b = btoa(array_to_string(hash_array)); //console.log("new function hash is "); //console.log(b); var call_code = ([ops.binary_op]).concat(integer_to_array(hash_size, 4)).concat(hash_array); var definition2 = replace(ops.recurse, call_code, definition); //var definition2 = replace(ops.recurse, ([ops.binary_op]).concat(integer_to_array(hash_size, 4)).concat(hash_array), definition); d.funs[b] = definition2; d.stack = ([["binary"].concat(hash_array)]).concat(d.stack); var s = definition2.length + 4; var mf = d.many_funs + 1; if (mf > d.fun_limit) { throw("too many functions error"); } else { d.many_funs = mf; } return {i: i, d: d, g: (s + 30), s: "define op", r: (s+s)}; } op_code[ops.define] = function(i, code, d) { var skipped_size = count_till(code, i, ops.fun_end); var definition = code.slice(i+1, i+skipped_size); //console.log("chalang define definition is "); //console.log(definition); //console.log(JSON.stringify(definition)); i += skipped_size; //var hash_array = small_hash(definition); var hash_array = hash(definition); var b = btoa(array_to_string(hash_array)); //console.log("new function hash is "); //console.log(b); var definition2 = replace(ops.recurse, ([ops.binary_op]).concat(integer_to_array(hash_size, 4)).concat(hash_array), definition); d.funs[b] = definition2; var s = definition2.length + 4; var mf = d.many_funs + 1; if (mf > d.fun_limit) { throw("too many functions error"); } else { d.many_funs = mf; } return {i: i, d: d, g: (s + 30), s: "define op", r: (s+s)}; } op_code[ops.print] = function(i, code, d) { console.log(JSON.stringify(d.stack)); return {i: i, d: d, g: 0, s: "print op"}; }; op_code[ops.drop] = function(i, code, d) { underflow_check(d, 1, "drop"); var m = memory(d.stack[0]); d.stack = d.stack.slice(1); return {i: i, d: d, g: 1, s: "drop op", r: (-2 - m)}; }; op_code[ops.dup] = function(i, code, d) { underflow_check(d, 1, "dup"); d.stack = ([d.stack[0]]).concat(d.stack); return {i: i, d: d, g: 1, s: "dup op", r: memory(d.stack[0])}; }; op_code[ops.swap] = function(i, code, d) { underflow_check(d, 2, "swap"); d.stack = ([d.stack[1]]).concat( [d.stack[0]]).concat( d.stack.slice(2)); return {i: i, d: d, g: 1, s: "swap op"}; }; op_code[ops.tuck] = function(i, code, d) { underflow_check(d, 3, "tuck"); d.stack = ([d.stack[1]]).concat( [d.stack[2]]).concat( [d.stack[0]]).concat( d.stack.slice(3)); return {i: i, d: d, g: 1, s: "tuck op"}; } op_code[ops.rot] = function(i, code, d) { underflow_check(d, 3, "rot"); d.stack = ([d.stack[2]]).concat( [d.stack[0]]).concat( [d.stack[1]]).concat( d.stack.slice(3)); return {i: i, d: d, g: 1, s: "rot op"}; } op_code[ops.ddup] = function(i, code, d) { underflow_check(d, 2, "ddup"); d.stack = d.stack.slice(0, 2).concat(d.stack); return {i: i, d: d, g: 1, s: "ddup op", r: (memory(d.stack[0]) + memory(d.stack[1]))}; } op_code[ops.tuckn] = function(i, code, d) { if (d.stack.length < 2) { throw("tuckn stack underflow"); } else { var n = d.stack[0]; underflow_check(d, 2+n,"tuckn"); d.stack = d.stack.slice(2, 2+n).concat( [d.stack[1]]).concat( d.stack.slice(3+n)); } return {i: i, d: d, g: 1, s: "tuckn op"}; } op_code[ops.pickn] = function(i, code, d) { var n = d.stack[0]; if (d.stack.length < (n + 1)) { throw("pickn stack underflow"); } else { d.stack = ([d.stack[n]]).concat( d.stack.slice(1, 1+n)).concat( d.stack.slice(2+n)); } return {i: i, d: d, g: 1, s: "pickn op"}; } op_code[ops.to_r] = function(i, code, d) { underflow_check(d, 1, "to_r"); d.alt = ([d.stack[0]]).concat(d.alt); d.stack = d.stack.slice(1); return {i: i, d: d, g: 1, s: ">r op"}; } op_code[ops.from_r] = function(i, code, d) { if (d.alt.length < 1) { throw(">r alt stack underflow"); } else { d.stack = ([d.alt[0]]).concat(d.stack); d.alt = d.alt.slice(1); } return {i: i, d: d, g: 1, s: "r> op"}; } op_code[ops.r_fetch] = function(i, code, d) { if (d.alt.length < 1) { throw("alt stack underflow"); } else { d.stack = ([d.alt[0]]).concat(d.stack); } return {i: i, d: d, g: 1, s: "r@ op"}; } op_code[ops.hash_op] = function(i, code, d) { underflow_check(d, 1, "hash"); d.stack = ([["binary"].concat(hash(d.stack[0].slice(1)))]).concat( d.stack.slice(1)); return {i: i, d: d, g: 20, s: "hash op"}; } op_code[ops.verify_sig] = function(i, code, d) { underflow_check(d, 3, "verify_sig"); //data, sig, key var pub1 = d.stack[0].slice(1);//internal format puts "binary" at the front of each binary. var data1 = d.stack[1].slice(1); var sig1 = d.stack[2].slice(1); var ec = keys.ec(), temp_key = ec.keyFromPublic(toHex(array_to_string(pub1)), "hex"); var sig2 = bin2rs(array_to_string(sig1)); var b = temp_key.verify(hash(serialize(data1)), sig2, "hex") var c; if (b) { c = 1; } else { c = 0; } d.stack = ([c]).concat( d.stack.slice(3)); return {i: i, d: d, g: 20, s: "verify_sig op"}; } op_code[ops.eq] = function(i, code, d) { underflow_check(d, 2, "eq"); if (JSON.stringify(d.stack[0]) == JSON.stringify(d.stack[1])) { d.stack = ([1]).concat(d.stack); } else { d.stack = ([0]).concat(d.stack); } return {i: i, d: d, g: 1, s: "eq op", r: 1}; } op_code[ops.eq2] = function(i, code, d) { underflow_check(d, 2, "eq"); if (JSON.stringify(d.stack[0]) == JSON.stringify(d.stack[1])) { d.stack = ([1]).concat(d.stack.slice(2)); } else { d.stack = ([0]).concat(d.stack.slice(2)); } return {i: i, d: d, g: 1, s: "eq op", r: 1}; } op_code[ops.bool_flip] = function(i, code, d) { underflow_check(d, 1, "bool_flip"); if (d.stack[0] == 0) { d.stack = ([1]).concat(d.stack.slice(1)); } else { d.stack = ([0]).concat(d.stack.slice(1)); } return {i: i, d: d, g: 1, s: "bool flip op"}; } op_code[ops.bool_and] = function(i, code, d) { underflow_check(d, 2, "bool_and"); if ((d.stack[0] == 0) || (d.stack[1] == 0)) { d.stack = ([0]).concat(d.stack.slice(2)); } else { d.stack = ([1]).concat(d.stack.slice(2)); } return {i: i, d: d, g: 1, s: "bool and op", r: (-2)}; } op_code[ops.bool_or] = function(i, code, d) { underflow_check(d, 2, "bool_or"); if ((d.stack[0] == 0) && (d.stack[1] == 0)) { d.stack = ([0]).concat(d.stack.slice(2)); } else { d.stack = ([1]).concat(d.stack.slice(2)); } return {i: i, d: d, g: 1, s: "bool or op", r: (-2)}; } op_code[ops.bool_xor] = function(i, code, d) { underflow_check(d, 2, "bool_xor"); var j = 0; if ((d.stack[0] == 0) && (d.stack[1] == 0)) { j = 0; } else if ((d.stack[0] == 0) || (d.stack[1] == 0)) { j=1; } d.stack = ([j]).concat(d.stack.slice(2)); return {i: i, d: d, g: 1, s: "bool xor op", r: (-2)}; } op_code[ops.stack_size] = function(i, code, d) { d.stack = ([d.stack.length]).concat(d.stack); return {i: i, d: d, g: 1, s: "stack_size op", r: 2}; } op_code[ops.height] = function(i, code, d) { d.stack = ([d.state.height]).concat(d.stack); return {i: i, d: d, g: 1, s: "height op", r: 2}; } op_code[ops.gas] = function(i, code, d) { d.stack = ([d.op_gas]).concat(d.stack); return {i: i, d: d, g: 1, s: "gas op", r: 2}; } op_code[ops.many_vars] = function(i, code, d) { d.stack = ([d.vars.length]).concat(d.stack); return {i: i, d: d, g: 1, s: "many vars op", r: 2}; } op_code[ops.many_funs] = function(i, code, d) { d.stack = (d.many_funs).concat(d.stack); return {i: i, d: d, g: 1, s: "many funs op", r: 2}; } op_code[ops.fun_end] = function(i, code, d) { return {i: i, d: d, g: 1, s: "fun end op"}; } op_code[ops.set] = function(i, code, d) { underflow_check(d, 2, "set"); d.vars[d.stack[0]] = d.stack[1]; d.stack = d.stack.slice(2); return {i: i, d: d, g: 1, s: "set op"}; } op_code[ops.fetch] = function(i, code, d) { underflow_check(d, 1, "fetch"); var val; var foo = d.vars[d.stack[0]]; if (foo == undefined) { val = []; } else { val = foo; } d.stack = ([val]).concat(d.stack.slice(1)); return {i: i, d: d, g: 1, s: "fetch op", r: (1+memory(val))}; } op_code[ops.cons] = function(i, code, d) { underflow_check(d, 2, "cons"); var l = ([d.stack[1]]).concat( d.stack[0]); d.stack = ([l]).concat( d.stack.slice(2)); return {i: i, d: d, g: 1, s: "cons op", r: 1}; } op_code[ops.car] = function(i, code, d) { underflow_check(d, 1, "car"); if (!(Array.isArray(d.stack[0]))) { console.log(JSON.stringify(d.stack)); throw("car op error"); } else { d.stack = ([d.stack[0].slice(1)]).concat( ([d.stack[0][0]])).concat( d.stack.slice(1)); } return {i: i, d: d, g: 1, s: "car op", r: (-1)}; } op_code[ops.empty_list] = function(i, code, d) { d.stack = ([[]]).concat(d.stack); return {i: i, d: d, g: 1, s: "empty list op", r: 1}; } op_code[ops.append] = function(i, code, d) { //console.log("append"); //console.log(JSON.stringify(d)); underflow_check(d, 2, "append"); var a; if (Number.isInteger(d.stack[0])) { d.stack[0] = (["binary"]).concat(integer_to_array(d.stack[0], 4)); } if (Number.isInteger(d.stack[1])) { d.stack[1] = (["binary"]).concat(integer_to_array(d.stack[1], 4)); } if (("binary" == d.stack[0][0]) && ("binary" == d.stack[1][0])) { a = (d.stack[1]).concat(d.stack[0].slice(1)); if (a.length == 5) { a = array_to_int(a.slice(1)); } } else if (!("binary" == d.stack[0][0]) && !("binary" == d.stack[1][0])) { a = (d.stack[1]).concat(d.stack[0]); } else { return ["error", "cannot append binary and list together", "append"]; } d.stack = ([a]).concat( d.stack.slice(2)); return {i: i, d: d, g: 1, s: "append op", r: 1}; } op_code[ops.split] = function(i, code, d) { underflow_check(d, 2, "split"); if (!(Array.isArray(d.stack[1]))) { //treat the integer like a 4 byte binary var n = d.stack[0]; var bin0 = integer_to_array(d.stack[1], 4); var bin1 = bin0.slice(0, n); var bin2 = bin0.slice(n, 4); d.stack = ([(["binary"]).concat(bin1)]).concat( ([(["binary"]).concat(bin2)]).concat(d.stack.slice(2))); } else if (!(d.stack[1][0] == "binary")) { throw("cannot split a list"); } else { var n = d.stack[0]; var bin1; if (n == 4) { bin1 = array_to_int(d.stack[1].slice(1, n+1)); } else { bin1 = d.stack[1].slice(0, n+1); } var bin2; if ((d.stack[1].length - n - 1) == 4) { bin2 = array_to_int(d.stack[1].slice(n+1)); } else { bin2 = (["binary"]).concat(d.stack[1].slice(n+1)); } d.stack = ([bin1]).concat( [bin2]).concat( d.stack.slice(2)); } return {i: i, d: d, g: 1, s: "split op", r: (-1)}; }; op_code[ops.reverse] = function(i, code, d) { underflow_check(d, 1, "reverse"); if (d.stack[0][0] == "binary") { return ["error", "cannot reverse a binary", "reverse"]; } else { d.stack = ([d.stack[0].reverse()]).concat( d.stack.slice(1)); } return {i: i, d: d, g: d.stack[0].length, s: "reverse op"}; }; op_code[ops.is_list] = function(i, code, d) { var j; underflow_check(d, 1, "is_list"); if (!(d.stack[0].is_array())) { j = 0; } else if (d.stack[0][0] == "binary") { j = 0; } else { j = 1; } d.stack = ([j]).concat(d.stack); return {i: i, d: d, g: 1, s: "is_list op", r: (-1)}; }; op_code[ops.nop] = function(i, code, d) { return {i: i, d: d, g: 0, s: "nop op"}; }; op_code[ops.fail] = function(i, code, d) { op_print(d, i, "fail op"); op_print(d, i, JSON.stringify(d.stack)); throw("fail error"); }; function run2(code, d) { //console.log("run 2"); for (var i = 0; i d.ram_most) { d.ram_most = d.ram_current; } if (d.op_gas < 0) { console.log(JSON.stringify(d)); console.log("out of time"); return ["error", "out of time"]; } else if (d.ram_current > d.ram_limit) { console.log("out of space. limit was: "); console.log(d.ram_limit); return ["error", "out of space"]; } else if ((code[i] == ops.call) && (code[i+1] == ops.fun_end)){ //tail call optimized function call //console.log("tail call optimized function call op"); //console.log(d.stack[0]); definition = d.funs[d.stack[0]]; var s = definition.length; d.op_gas = d.op_gas - s - 10; d.ram_current = d.ram_current + s - 1; d.stack = d.stack.slice(1); code = definition.concat(code.slice(i+1)); i = 0; op_print(d, i, "optimized call op"); //return run2(definition.concat(rest), d); } else if (code[i] == ops.finish) { op_print(d, i, "return op"); return d; } else if ((!(code[i] < ops.add)) && (code[i] < ops.eq)) { //console.log("arithmetic"); underflow_check(d, 2, "arithmetic"); d.op_gas = d.op_gas - 1; d.ram_current = d.ram_current - 2; var a = arithmetic_chalang(code[i], d.stack[0], d.stack[1]); d.stack = a.concat(d.stack.slice(2)); op_print(d, i, ("math ").concat((code[i]).toString())); } else if ((code[i]>139) && (code[i]<176)){ d.op_gas = d.op_gas - 1; d.ram_current = d.ram_current - 1; var a = code[i] - 140; d.stack = [a].concat(d.stack); } else { var y = op_code[code[i]](i, code, d); //console.log("return after calling function definition "); //console.log(JSON.stringify(y)); i = y.i; d = y.d; d.op_gas -= y.g; if (!(y.r == undefined)) { d.ram_current += y.r; } op_print(d, i, y.s); } } return d; } function is_balanced_f(code) { var x = 0; for (var i = 0; i> ops.binary_op, 0,0,0,3, 1,2,3, ops.int4, 0,0,0,1, ops.split, ops.append ]; function chalang_test() { var d = data_maker(1000, 1000, 50, 1000, [], [], new_state(0, 0)); console.log("chalang test"); //var x = run5(verify_signature_contract, d); //var x = run5(case_contract, d); //var x = run5(hashlock_contract, d); //var x = run5(split_append_contract, d); //var x = run5(recursion_contract, d); //var x = run5(variable_contract, d); //var x = run5(function_contract, d); var x = run5(function_contract2, d); //var x = run5(map_contract, d); console.log(JSON.stringify(x.stack)); return x.stack; } function new_state(height, slash) { return{"name": "state", "height": height, "slash": slash}; } function data_maker(op_gas, ram_gas, many_vs, many_funs, script_sig, code, state) { //console.log("data maker vars "); //console.log(many_vs); var arr = []; arr.length = Math.min(200, many_vs); return {"name": "d", "op_gas":op_gas, "stack": [], "alt": [], "ram_most": 0, "ram_limit":ram_gas, "vars": arr, "funs":{}, "many_funs": 0, "fun_limit":many_funs, "ram_current":(script_sig.length + code.length), "state":state}; } return {run5: run5, test: chalang_test, ops: function() {return(ops);}, new_state: new_state, data_maker: data_maker}; } var chalang_object = chalang_main(); //var foo = chalang_object.test();//this is how you make the test run. //console.log(JSON.stringify(foo));