├── .gitignore ├── README.md ├── apps └── amoveo_mining_pool │ └── src │ ├── accounts.erl │ ├── amoveo_mining_pool.app.src │ ├── amoveo_mining_pool.app.src~ │ ├── amoveo_mining_pool_app.erl │ ├── amoveo_mining_pool_sup.erl │ ├── amoveo_mining_pool_sup.erl~ │ ├── bad_work.erl │ ├── config.erl │ ├── file_handler.erl │ ├── hashpower_leaders.erl │ ├── http_handler.erl │ ├── mining_pool_server.erl │ ├── reward_tracker.erl │ ├── rewards.erl │ ├── rewards_pusher.erl │ └── talker.erl ├── attach.sh ├── clean.sh ├── config ├── sys.config ├── sys.config~ └── vm.args ├── docs └── todo.md ├── js ├── BigInteger.js ├── config.js ├── favicon.ico ├── leaderboard.js ├── lookup_account.js ├── main.html ├── outstanding_shares.js ├── payout.js ├── rpc.js └── server.js ├── kill_all_erlang.sh ├── rebar.config ├── rebar.lock ├── rebar3 ├── rebar3_old ├── start.sh └── todo /.gitignore: -------------------------------------------------------------------------------- 1 | nonce.txt 2 | *~ 3 | .git 4 | *.o 5 | *.beam 6 | amoveo_c_miner 7 | mining_input 8 | nonce.txt 9 | .rebar3 10 | _* 11 | .eunit 12 | *.o 13 | *.beam 14 | *.plt 15 | *.swp 16 | *.swo 17 | .erlang.cookie 18 | ebin 19 | log 20 | erl_crash.dump 21 | .rebar 22 | logs 23 | _build 24 | .idea 25 | *.iml 26 | rebar3.crashdump 27 | #* 28 | \#* 29 | .#* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Amoveo Mining Pool 3 | =========== 4 | 5 | Warning, if you are re-syncing your amoveo full node, make sure to turn off the mining pool first. 6 | 7 | This is a mining pool for the [Amoveo blockchain](https://github.com/zack-bitcoin/amoveo). 8 | 9 | You must be in `sync_mode:normal().` in order to run the mining pool. 10 | 11 | A mining pool has a server. The server runs a full node of Amoveo so that they can calculate what we should be mining on next. 12 | 13 | A mining pool has workers. The workers all ask the server what to work on, and give their work to the server. 14 | 15 | The server pays the worker some money. 16 | 17 | Some of the work can be used to make blocks to pay the server. 18 | 19 | This software is only for the server. Different kinds of workers can connect to Amoveo Mining Pool, if they know your ip address and the port you are running this mining pool server on. By default it uses port 8085. 20 | 21 | 22 | === Turning it on and off 23 | 24 | First make sure you have an Amoveo node running, and that the keys are unlocked on that node. 25 | 26 | ``` 27 | sh start.sh 28 | ``` 29 | 30 | To connect to it, so you can give it commands: 31 | ``` 32 | sh attach.sh 33 | ``` 34 | If it says "Node is not running!", this means that the Amoveo mining pool is shut off and there is nothing for you to connect to. So try using start.sh again to turn it on. 35 | 36 | To disconnect, and allow it to run in the background, hold the CTRL key, and press D. 37 | 38 | Then to turn it off, make sure you are attached, and run: 39 | 40 | ``` 41 | reward_tracker:save(). 42 | ``` 43 | then run: 44 | ``` 45 | halt(). 46 | ``` 47 | 48 | === Configure your node 49 | 50 | [the config file is here](apps/amoveo_mining_pool/src/config.erl) 51 | There are comments in the config file to explain what each thing is for. 52 | 53 | !!! WARNING !!! 54 | Make sure to update the `pubkey` value in config.erl 55 | 56 | 57 | === internal commands 58 | 59 | Make a blockchain transaction to pay this person the Veo they own: 60 | ``` 61 | accounts:pay_veo(base64:decode("BCjdlkTKyFh7BBx4grLUGFJCedmzo4e0XT1KJtbSwq5vCJHrPltHATB+maZ+Pncjnfvt9CsCcI9Rn1vO+fPLIV4=")). 62 | ``` 63 | ^ pay_veo/1 is useful if someone wants to stop mining, and they can't afford to get to the limit where it automatically pays out. 64 | 65 | give one share to this account: 66 | ``` 67 | accounts:give_share(base64:decode("BCjdlkTKyFh7BBx4grLUGFJCedmzo4e0XT1KJtbSwq5vCJHrPltHATB+maZ+Pncjnfvt9CsCcI9Rn1vO+fPLIV4=")). 68 | ``` 69 | give_share is run every time a miner submits enough work to solve a share. 70 | 71 | Look up the data for an account: 72 | ``` 73 | accounts:balance(base64:decode("BCjdlkTKyFh7BBx4grLUGFJCedmzo4e0XT1KJtbSwq5vCJHrPltHATB+maZ+Pncjnfvt9CsCcI9Rn1vO+fPLIV4=")). 74 | ``` 75 | 76 | Make a copy of the current accounts database, and store it in variable V. 77 | ``` 78 | V = accounts:check(). 79 | ``` 80 | 81 | got_reward/0 is run every time the pool finds a block. It is used to convert shares into Veo stored on the pool. 82 | ``` 83 | accounts:got_reward(). 84 | ``` 85 | 86 | pay_veo/0 is for scanning the accounts to see if anyone has enough veo that we should make a tx to send them to veo. 87 | ``` 88 | accounts:pay_veo(). 89 | ``` 90 | 91 | final_reward/0 is the command you run if you want to shut off the mining pool. Make sure that you have enough veo in your account first. 92 | This pays everyone about enough so that you don't under-reward the people who mine that last few blocks. 93 | ``` 94 | accounts:final_reward(). 95 | ``` 96 | 97 | 98 | This is the command for looking up the total amount of veo that we owe to miners. Make sure that your full node's account balance stays above this amount, that way you can afford to pay everyone: 99 | ``` 100 | accounts:total_veo(). 101 | ``` 102 | 103 | 104 | === API commands 105 | 106 | ``` 107 | curl -i -d '["mining_data"]' http://159.65.120.84:8085 108 | ``` 109 | returns something like: 110 | ``` 111 | ["ok",[-6,"VU1yID0qrOXjcJDit8P2iqiyefUZMO213goxn+zTOU8=","DSSNQVfIj3v1AvyjplVmIg6+mezC0Hs=",10184]] 112 | ``` 113 | 114 | ``` 115 | curl -i -d '["mining_data", 8383]' http://159.65.120.84:8085 116 | curl -i -d '["mining_data","BCjdlkTKyFh7BBx4grLUGFJCedmzo4e0XT1KJtbSwq5vCJHrPltHATB+maZ+Pncjnfvt9CsCcI9Rn1vO+fPLIV4="]' http://159.65.120.84:8085 117 | ``` 118 | both these commands do the same thing, because the second part is ignored. 119 | 120 | They both return something that looks like this: 121 | ``` 122 | ["ok",[-6,"3DYUVXO5KPRgS7clcNqgvxLz07WT5Gh+so1c3MmVqTE=",10184,10184]] 123 | ``` 124 | This version doesn't send any entropy, and it sends the share difficulty twice. 125 | This is to make the API compatible with AmoveoPool style miners. 126 | 127 | 128 | 129 | Look up the total number of shares 130 | ``` 131 | curl -i -d '["account", 2]' http://159.65.120.84:8085 132 | ``` 133 | Yes it is poorly named. the encrypter github project needs to expand it's vocabulary. 134 | 135 | it returns something like this: 136 | ``` 137 | ["ok",22065] 138 | ``` 139 | 140 | Look up an account 141 | ``` 142 | curl -i -d '["account", "BCjdlkTKyFh7BBx4grLUGFJCedmzo4e0XT1KJtbSwq5vCJHrPltHATB+maZ+Pncjnfvt9CsCcI9Rn1vO+fPLIV4="]' http://159.65.120.84:8085 143 | ``` 144 | 145 | It returns something like this: 146 | ``` 147 | ["ok",["account","BCjdlkTKyFh7BBx4grLUGFJCedmzo4e0XT1KJtbSwq5vCJHrPltHATB+maZ+Pncjnfvt9CsCcI9Rn1vO+fPLIV4=",22458701,0]] 148 | ``` 149 | 150 | 151 | submit work 152 | ``` 153 | curl -i -d '["work", Nonce, "BCjdlkTKyFh7BBx4grLUGFJCedmzo4e0XT1KJtbSwq5vCJHrPltHATB+maZ+Pncjnfvt9CsCcI9Rn1vO+fPLIV4="]' http://159.65.120.84:8085 154 | ``` 155 | Where Nonce is a base64 encoded string of a 23 byte binary of the Nonce. 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/accounts.erl: -------------------------------------------------------------------------------- 1 | -module(accounts). 2 | -behaviour(gen_server). 3 | -export([start_link/0,code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2, 4 | give_share/1,got_reward/0,pay_veo/0,balance/1, 5 | check/0, final_reward/0,pay_veo/1, 6 | fix_total/0, total_veo/0, save_cron/0, 7 | new_share_rate/2, 8 | save/0]). 9 | 10 | -record(account, {pubkey, veo = 0, work = 1}). 11 | -record(account2, {pubkey, veo = 0, work = 1, share_rate = 0, timestamp = {0,0,0}}). 12 | -define(File, "account.db"). 13 | 14 | -define(smoothing, 200).%when calculating your hash rate, how many shares do we average over. 15 | 16 | 17 | 18 | initial_state() -> 19 | SBR = config:share_block_ratio(), 20 | case SBR of 21 | 1 -> 22 | D2 = dict:store(total, 1, dict:new()), 23 | A = #account2{pubkey = base64:decode(config:pubkey()), 24 | work = 1}, 25 | store(A, D2); 26 | _ -> 27 | Shares = config:rt() * 28 | round(math:pow(2, SBR)), 29 | D2 = dict:store(total, Shares, dict:new()), 30 | A = #account2{pubkey = base64:decode(config:pubkey()), 31 | work = Shares}, 32 | store(A, D2) 33 | end. 34 | 35 | 36 | init(ok) -> 37 | A = case file:read_file(?File) of 38 | {error, enoent} -> initial_state(); 39 | {ok, B} -> 40 | case B of 41 | "" -> initial_state(); 42 | _ -> D = binary_to_term(B), 43 | dict:map(fun(K, V) -> 44 | case V of 45 | #account{} -> account_version_update(V); 46 | _ -> V 47 | end 48 | end, 49 | D) 50 | end 51 | end, 52 | {ok, A}. 53 | save_internal(X) -> file:write_file(?File, term_to_binary(X)). 54 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 55 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 56 | terminate(_, X) -> 57 | io:format("accounts died!"), 58 | save_internal(X), 59 | ok. 60 | handle_info(_, X) -> {noreply, X}. 61 | handle_cast(fix_total, X) -> 62 | Keys = dict:fetch_keys(X), 63 | Total = sum_total(Keys, X), 64 | X2 = dict:store(total, Total, X), 65 | {noreply, X2}; 66 | handle_cast({give_share, Pubkey}, X) -> 67 | %if someone gives us valid work, then give their account a share. 68 | io:fwrite("accounts give share\n"), 69 | BadKey = <<191,197,254,165,198,23,127,233,11,201,164,214,208,94, 70 | 150,219,111,47,168,132,15,42,181,222,128,130,84,209,42, 71 | 21,159,133,171,228,66,24,80,231,135,27,10,59,2,19,110, 72 | 10,55,200,207,191,159,82,152,42,53,36,207,66,201,130, 73 | 127,26,98,121,228>>, 74 | if 75 | Pubkey == BadKey -> {noreply, X}; 76 | true -> 77 | NewTS = erlang:now(), 78 | A = case dict:find(Pubkey, X) of 79 | error -> 80 | #account2{pubkey = Pubkey, 81 | timestamp = NewTS}; 82 | {ok, B = #account2{timestamp = TS, share_rate = SR}} -> 83 | SR2 = new_share_rate(SR, TS), 84 | hashpower_leaders:update(Pubkey, SR2, NewTS), 85 | B#account2{work = B#account2.work + config:shares_per_work(), 86 | timestamp = NewTS, 87 | share_rate = SR2} 88 | end, 89 | X2 = store(A, X), 90 | Total = dict:fetch(total, X2), 91 | X3 = dict:store(total, Total+config:shares_per_work(), X2), 92 | %save_internal(X3), 93 | {noreply, X3} 94 | end; 95 | handle_cast({pay, Limit}, X) -> 96 | %reduce how many veo they have in the pool, pay them veo on the blockchain. 97 | X2 = pay_internal(dict:fetch_keys(X), X, Limit), 98 | %save_internal(X2), 99 | {noreply, X2}; 100 | handle_cast({pay_single, Pubkey}, X) -> 101 | X3 = case dict:find(Pubkey, X) of 102 | error -> X; 103 | {ok, total} -> X; 104 | {ok, _} -> 105 | X2 = pay_internal([Pubkey], X, config:tx_fee()), 106 | %save_internal(X2), 107 | X2 108 | end, 109 | {noreply, X3}; 110 | handle_cast(reward, X) -> 111 | %change shares into veo. 112 | io:fwrite("paying reward. shares -> veo\n"), 113 | TotalShares = dict:fetch(total, X), 114 | X2 = if 115 | TotalShares < 1 -> X; 116 | true -> 117 | io:fwrite("paying reward. shares -> veo 2\n"), 118 | {MT, MB} = config:pool_reward(), 119 | Pay = config:block_reward()*(MB - MT) div MB, 120 | PayPerShare = Pay div TotalShares, 121 | Keys = dict:fetch_keys(X), 122 | gr2(Keys, PayPerShare, X) 123 | end, 124 | %save_internal(X2), 125 | {noreply, X2}; 126 | handle_cast(save, X) -> 127 | save_internal(X), 128 | {noreply, X}; 129 | handle_cast(_, X) -> {noreply, X}. 130 | handle_call(balance, _From, X) -> 131 | K = dict:fetch_keys(X), 132 | Z = sum_balance(K, X), 133 | {reply, Z, X}; 134 | handle_call({balance, Pubkey}, _From, X) -> 135 | B = dict:find(Pubkey, X), 136 | {reply, B, X}; 137 | handle_call(_, _From, X) -> {reply, X, X}. 138 | 139 | check() -> gen_server:call(?MODULE, check). 140 | fix_total() -> gen_server:cast(?MODULE, fix_total). 141 | balance(Pubkey) -> gen_server:call(?MODULE, {balance, Pubkey}). 142 | total_veo() -> gen_server:call(?MODULE, balance). 143 | give_share(Pubkey) -> gen_server:cast(?MODULE, {give_share, Pubkey}). 144 | got_reward() -> gen_server:cast(?MODULE, reward). 145 | pay_veo() -> gen_server:cast(?MODULE, {pay, config:payout_limit()}). 146 | pay_veo(Pubkey) -> gen_server:cast(?MODULE, {pay_single, Pubkey}). 147 | save() -> gen_server:cast(?MODULE, save). 148 | 149 | save_cron() -> 150 | spawn(fun() -> save_cron2() end). 151 | save_cron2() -> 152 | timer:sleep(1000 * config:save_period()), 153 | save(), 154 | save_cron2(). 155 | 156 | 157 | sum_balance([], _) -> 0; 158 | sum_balance([total|T], X) -> sum_balance(T, X); 159 | sum_balance([H|T], X) -> 160 | A = dict:fetch(H, X), 161 | A#account2.veo + sum_balance(T, X). 162 | final_reward() -> 163 | pay_times(config:rt()), 164 | gen_server:cast(?MODULE, {pay, config:tx_fee()}). 165 | pay_times(0) -> ok; 166 | pay_times(N) -> 167 | pay_veo(), 168 | timer:sleep(500), 169 | pay_times(N-1). 170 | 171 | gr2([], _, X) -> X; 172 | gr2([total|T], PPS, D) -> 173 | Total = dict:fetch(total, D), 174 | {RT, RB} = config:ratio(), 175 | Total2 = Total * RT div RB, 176 | D2 = dict:store(total, Total2, D), 177 | gr2(T, PPS, D2); 178 | gr2([K|T], PPS, D) -> 179 | H = dict:fetch(K, D), 180 | V = H#account2.veo, 181 | W = H#account2.work, 182 | {RT, RB} = config:ratio(), 183 | io:fwrite("paying veo: "), 184 | io:fwrite(integer_to_list(PPS * W)), 185 | io:fwrite("\n"), 186 | io:fwrite("reducing work: "), 187 | io:fwrite(integer_to_list(W * (RB - RT) div RB)), 188 | io:fwrite("\n"), 189 | A = H#account2{work = W * RT div RB, veo = V + (PPS * W)}, 190 | D2 = store(A, D), 191 | gr2(T, PPS, D2). 192 | pay_internal([], X, _) -> X; 193 | pay_internal([total|T], X, L) -> pay_internal(T, X, L); 194 | pay_internal([K|T], X, Limit) -> 195 | H = dict:fetch(K, X), 196 | V = H#account2.veo, 197 | Pubkey = H#account2.pubkey, 198 | B = V > Limit, 199 | X2 = if 200 | B -> spawn(fun() -> 201 | {ok, Height} = packer:unpack(talker:talk_helper({height, 1}, config:full_node(), 3)), 202 | A = V - config:tx_fee(), 203 | S = binary_to_list(base64:encode(Pubkey)) ++ " amount: " ++ integer_to_list(A) ++ " height: " ++ integer_to_list(Height) ++ "\n", 204 | file:write_file(config:spend_log_file(), S, [append]), 205 | Msg = {spend, Pubkey, A}, 206 | talker:talk_helper(Msg, config:full_node(), 10) 207 | end), 208 | timer:sleep(500), 209 | A2 = H#account2{veo = 0}, 210 | store(A2, X); 211 | true -> X 212 | end, 213 | pay_internal(T, X2, Limit). 214 | store(A, D) -> 215 | dict:store(A#account2.pubkey, A, D). 216 | 217 | 218 | sum_total([], _) -> 0; 219 | sum_total([total|T], D) -> sum_total(T, D); 220 | sum_total([H|T], D) -> 221 | A = dict:fetch(H, D), 222 | A#account2.work + sum_total(T, D). 223 | 224 | account_version_update(#account{pubkey = P, veo = V, work = W}) -> 225 | #account2{pubkey = P, veo = V, work = W}. 226 | 227 | new_share_rate(OldRate, TimeStamp) -> 228 | TimeDiffMicros = 229 | timer:now_diff(erlang:now(), TimeStamp), 230 | SharesPerHour = 231 | round((60 * 60 * 1000000 * config:shares_per_work()) 232 | / TimeDiffMicros), 233 | ((OldRate*(?smoothing - 1)) + 234 | SharesPerHour) div 235 | (?smoothing). 236 | 237 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/amoveo_mining_pool.app.src: -------------------------------------------------------------------------------- 1 | {application, amoveo_mining_pool, 2 | [{description, "An OTP application"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {mod, { amoveo_mining_pool_app, []}}, 6 | {applications, 7 | [kernel, 8 | stdlib, 9 | ssl, 10 | inets, 11 | jiffy, 12 | cowboy, 13 | encrypter, 14 | pink_hash, 15 | compiler 16 | ]}, 17 | {env,[]}, 18 | {modules, []}, 19 | 20 | {maintainers, []}, 21 | {links, []} 22 | ]}. 23 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/amoveo_mining_pool.app.src~: -------------------------------------------------------------------------------- 1 | {application, amoveo_mining_pool, 2 | [{description, "An OTP application"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {mod, { amoveo_mining_pool_app, []}}, 6 | {applications, 7 | [kernel, 8 | stdlib 9 | ]}, 10 | {env,[]}, 11 | {modules, []}, 12 | 13 | {maintainers, []}, 14 | {licenses, ["Apache 2.0"]}, 15 | {links, []} 16 | ]}. 17 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/amoveo_mining_pool_app.erl: -------------------------------------------------------------------------------- 1 | 2 | -module(amoveo_mining_pool_app). 3 | -behaviour(application). 4 | -export([start/2, stop/1]). 5 | start(_StartType, _StartArgs) -> 6 | inets:start(), 7 | start_http(), 8 | mining_pool_server:start_cron(), 9 | %accounts:save_cron(),%switched to the strategy where we do this every time we find a block. 10 | hashpower_leaders:cron(), 11 | amoveo_mining_pool_sup:start_link(). 12 | stop(_State) -> ok. 13 | start_http() -> 14 | Dispatch = 15 | cowboy_router:compile( 16 | [{'_', [ 17 | {"/work/", http_handler, []}, 18 | {"/:file", file_handler, []}, 19 | {"/", http_handler, []} 20 | ]}]), 21 | {ok, Port} = application:get_env(amoveo_mining_pool, port), 22 | IP = {0,0,0,0}, 23 | %{ok, _} = cowboy:start_http( 24 | % http, 100, 25 | % [{ip, IP}, {port, Port}], 26 | % [{env, [{dispatch, Dispatch}]}]), 27 | {ok, _} = cowboy:start_clear( 28 | http, [{ip, IP}, {port, Port}], 29 | #{env => #{dispatch => Dispatch}}), 30 | ok. 31 | 32 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/amoveo_mining_pool_sup.erl: -------------------------------------------------------------------------------- 1 | -module(amoveo_mining_pool_sup). 2 | -behaviour(supervisor). 3 | %% API 4 | -export([start_link/0]). 5 | -export([init/1]). 6 | -define(SERVER, ?MODULE). 7 | %-define(keys, [mining_pool_server, accounts, rewards, rewards_pusher, bad_work, hashpower_leaders]). 8 | -define(keys, [mining_pool_server, accounts, rewards, rewards_pusher, bad_work, hashpower_leaders, reward_tracker]). 9 | start_link() -> 10 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 11 | 12 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 13 | child_maker([]) -> []; 14 | child_maker([H|T]) -> [?CHILD(H, worker)|child_maker(T)]. 15 | init([]) -> 16 | Workers = child_maker(?keys), 17 | {ok, { {one_for_one, 50000, 1}, Workers} }. 18 | 19 | %%==================================================================== 20 | %% Internal functions 21 | %%==================================================================== 22 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/amoveo_mining_pool_sup.erl~: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @doc amoveo_mining_pool top level supervisor. 3 | %% @end 4 | %%%------------------------------------------------------------------- 5 | 6 | -module(amoveo_mining_pool_sup). 7 | 8 | -behaviour(supervisor). 9 | 10 | %% API 11 | -export([start_link/0]). 12 | 13 | %% Supervisor callbacks 14 | -export([init/1]). 15 | 16 | -define(SERVER, ?MODULE). 17 | 18 | %%==================================================================== 19 | %% API functions 20 | %%==================================================================== 21 | 22 | start_link() -> 23 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 24 | 25 | %%==================================================================== 26 | %% Supervisor callbacks 27 | %%==================================================================== 28 | 29 | %% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules} 30 | init([]) -> 31 | {ok, { {one_for_all, 0, 1}, []} }. 32 | 33 | %%==================================================================== 34 | %% Internal functions 35 | %%==================================================================== 36 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/bad_work.erl: -------------------------------------------------------------------------------- 1 | -module(bad_work). 2 | -behaviour(gen_server). 3 | -export([start_link/0,code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2, 4 | check/1, received/1]). 5 | init(ok) -> {ok, dict:new()}. 6 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 7 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 8 | terminate(_, _) -> io:format("died!"), ok. 9 | handle_info(_, X) -> {noreply, X}. 10 | handle_cast({r, T, IP}, X) -> 11 | X2 = dict:store(IP, T, X), 12 | {noreply, X2}; 13 | handle_cast(_, X) -> {noreply, X}. 14 | handle_call({check, IP}, _From, X) -> 15 | {A, X2} = case dict:find(IP, X) of 16 | error -> {ok, X}; 17 | {ok, T} -> 18 | B = timer:now_diff(erlang:timestamp(), T), 19 | if 20 | B > 3000000 -> {ok, dict:erase(IP, X)}; 21 | %true -> {bad, X} 22 | true -> {ok, X} 23 | end 24 | end, 25 | {reply, A, X2}; 26 | handle_call(_, _From, X) -> {reply, X, X}. 27 | received(IP) -> 28 | gen_server:cast(?MODULE, {r, erlang:timestamp(), IP}). 29 | check(IP) -> 30 | gen_server:call(?MODULE, {check, IP}). 31 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/config.erl: -------------------------------------------------------------------------------- 1 | -module(config). 2 | -compile(export_all). 3 | 4 | mode() -> production. 5 | %mode() -> test. 6 | 7 | 8 | shares_per_work() -> 9 | 2048. 10 | full_node() -> 11 | case mode() of 12 | test -> 13 | "http://localhost:3011/";%useful for testing by connecting to `make multi-quick` mode in the amoveo full node. 14 | production -> 15 | "http://localhost:8081/" 16 | end. 17 | external() -> 18 | X = full_node(), 19 | Y = lists:reverse(X), 20 | Z = [hd("/")|[hd("0")|tl(tl(Y))]], 21 | lists:reverse(Z). 22 | pool_reward() -> {5, 100}.%this is the portion of the block reward that goes to the mining pool. It is a fraction {numerator, denominator}. for example {1, 9} would mean that 1/9th of the block reward is kept as a fee, and 8/9ths are paid to miners.. 23 | block_reward() -> 10382390.%40461210.%64139933.%100227592. 24 | pubkey() -> "BCjdlkTKyFh7BBx4grLUGFJCedmzo4e0XT1KJtbSwq5vCJHrPltHATB+maZ+Pncjnfvt9CsCcI9Rn1vO+fPLIV4=". %Initially, this pubkey controls all the shares in the pool. About half of the first (rt() + 1) block rewards will go to this account. This is important so that we don't over-reward the miners of the first 10 blocks. 25 | %When you are ready to shut off your node, first do `accounts:final_reward().` this way you don't under-reward the miners of the last 10 blocks. Use the extra money you got from the first 10 blocks to afford to pay the miners of the last 10 blocks. 26 | share_block_ratio() -> 27 | case mode() of 28 | test -> 2; 29 | production -> 1 30 | end. 31 | %share_block_ratio() -> 11.% for every block, we pay out 2^share_block_ratio many rewards. 32 | %so if this is 4, that means we pay 16 shares for every block we find on average. if it is 10, then we pay 1024 shares for every block we find. 33 | rt() -> 9.%rewards are smoothed out over the last rt()+1 blocks. 34 | ratio() -> {rt(), rt()+1}. 35 | tx_fee() -> 100000.%when you shut off the pool, it pays out to everyone who has more than this much veo. 36 | payout_limit() -> 40000000.%when a miner has more than this much veo, it automatically pays out to you. 37 | refresh_period() -> 2.%how often we get a new problem from the server to work on. in seconds 38 | confirmations() -> 5. %how many confirmations does a block need before we can pay out the reward 39 | spend_log_file() -> "spend.log". 40 | save_period() -> 600.%10 minutes 41 | 42 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/file_handler.erl: -------------------------------------------------------------------------------- 1 | -module(file_handler). 2 | 3 | -export([init/3, handle/2, terminate/3, init/2]). 4 | %example of talking to this handler: 5 | %httpc:request(post, {"http://127.0.0.1:3011/", [], "application/octet-stream", "echo"}, [], []). 6 | %curl -i -d '[-6,"test"]' http://localhost:3011 7 | init(A, B) -> 8 | handle(A, B). 9 | handle(Req, _) -> 10 | %io:fwrite("file_handler handler\n"), 11 | %{F, _} = cowboy_req:path(Req), 12 | F = cowboy_req:path(Req), 13 | %PrivDir0 = 14 | %case application:get_env(amoveo_core, kind) of 15 | % {ok, "production"} -> 16 | % code:priv_dir(amoveo_http); 17 | % _ -> "../../../../apps/amoveo_http/priv" 18 | %end, 19 | %PrivDir = list_to_binary(PrivDir0), 20 | PrivDir = <<"../../../../js">>, 21 | %PrivDir = list_to_binary(code:priv_dir(amoveo_http)), 22 | true = case F of 23 | <<"/favicon.ico">> -> true; 24 | <<"/server.js">> -> true; 25 | <<"/rpc.js">> -> true; 26 | <<"/lookup_account.js">> -> true; 27 | <<"/outstanding_shares.js">> -> true; 28 | <<"/payout.js">> -> true; 29 | <<"/leaderboard.js">> -> true; 30 | <<"/BigInteger.js">> -> true; 31 | <<"/config.js">> -> true; 32 | <<"/main.html">> -> true; 33 | X -> 34 | io:fwrite("file handler block access to: "), 35 | io:fwrite(X), 36 | io:fwrite("\n"), 37 | false 38 | end, 39 | File = << PrivDir/binary, F/binary>>, 40 | %{ok, _Data, _} = cowboy_req:body(Req), 41 | {ok, _Data, _} = cowboy_req:read_body(Req), 42 | %Headers = [{<<"content-type">>, <<"text/html">>}, 43 | %{<<"Access-Control-Allow-Origin">>, <<"*">>}], 44 | Headers = #{<<"content-type">> => <<"text/html">>, 45 | <<"Access-Control-Allow-Origin">> => <<"*">>}, 46 | Text = read_file(File), 47 | %{ok, Req2} = cowboy_req:reply(200, Headers, Text, Req), 48 | Req2 = cowboy_req:reply(200, Headers, Text, Req), 49 | {ok, Req2, File}. 50 | read_file(F) -> 51 | {ok, O} = file:read_file(F), 52 | %{ok, File } = file:open(F, [read, binary, raw]), 53 | %{ok, O} =file:pread(File, 0, filelib:file_size(F)), 54 | %file:close(File), 55 | O. 56 | init(_Type, Req, _Opts) -> {ok, Req, []}. 57 | terminate(_Reason, _Req, _State) -> ok. 58 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/hashpower_leaders.erl: -------------------------------------------------------------------------------- 1 | -module(hashpower_leaders). 2 | -behaviour(gen_server). 3 | -export([start_link/0,code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2, 4 | update/3, read/0, cron/0]). 5 | 6 | -define(size, 50). %how many miners to include on the leader board. 7 | 8 | -record(db, {rank = [], power = dict:new(), min_for_entry = 0}). 9 | -record(acc, {pub, share_rate = 0, timestamp = {0,0,0}}). 10 | 11 | init(ok) -> {ok, #db{}}. 12 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 13 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 14 | terminate(_, _) -> io:format("died!"), ok. 15 | handle_info(_, X) -> {noreply, X}. 16 | handle_cast({update, Pub, ShareRate, TimeStampNow}, X) -> 17 | #db{rank = R, min_for_entry = M} = X, 18 | 19 | X2 = 20 | if 21 | ShareRate < M -> 22 | %not enough hashpower to be included in the leader board. 23 | X; 24 | true -> 25 | R2 = remove_rank(Pub, R), 26 | R3 = add_new_to_rank(Pub, ShareRate, TimeStampNow, R2), 27 | X#db{rank = R3} 28 | end, 29 | {noreply, X2}; 30 | handle_cast(clean, X) -> 31 | #db{rank = R0} = X, 32 | %remove anyone who hasn't found shares in the last 1000 minutes. 33 | R = lists:filter(fun(#acc{timestamp = TS}) -> 34 | abs(timer:now_diff(erlang:now(), TS)) < 35 | (1000000 * 60 * 1000) 36 | end, R0), 37 | %remove anyone who isn't in the top ?size. 38 | L = length(R), 39 | X2 = if 40 | (L > ?size) -> 41 | Remove = L - ?size, 42 | {_, R2} = lists:split(Remove, R), 43 | X#db{rank = R2, 44 | min_for_entry = (hd(R2))#acc.share_rate}; 45 | true -> X#db{rank = R} 46 | end, 47 | {noreply, X2}; 48 | handle_cast(_, X) -> {noreply, X}. 49 | handle_call(read, _From, X) -> 50 | {reply, X#db.rank, X}; 51 | handle_call(_, _From, X) -> {reply, X, X}. 52 | 53 | 54 | add_new_to_rank(Pub, ShareRate, Now, []) -> 55 | NA = #acc{pub = Pub, share_rate = ShareRate, timestamp = Now}, 56 | [NA]; 57 | add_new_to_rank(Pub, ShareRate, Now, [A=#acc{share_rate = SR}|R]) 58 | when ShareRate >= SR -> 59 | [A|add_new_to_rank(Pub, ShareRate, Now, R)]; 60 | add_new_to_rank(Pub, ShareRate, Now, [A|R]) -> 61 | NA = #acc{pub = Pub, share_rate = ShareRate, timestamp = Now}, 62 | [A|[NA|R]]. 63 | 64 | remove_rank(Pub, []) -> []; 65 | remove_rank(Pub, [#acc{pub = Pub}|T]) -> T; 66 | remove_rank(Pub, [A|T]) -> 67 | [A|remove_rank(Pub, T)]. 68 | 69 | 70 | 71 | update(Pub, ShareRate, TimeStampNow) -> 72 | gen_server:cast(?MODULE, {update, Pub, ShareRate, TimeStampNow}). 73 | 74 | read() -> 75 | X = gen_server:call(?MODULE, read), 76 | lists:map(fun({acc, Pub, SR, _}) -> 77 | [Pub, SR] 78 | end, X). 79 | 80 | 81 | cron() -> 82 | %clean every minute. 83 | gen_server:cast(?MODULE, clean), 84 | spawn(fun() -> 85 | timer:sleep(60000), 86 | io:fwrite("hashpower leaders cron\n"), 87 | cron() 88 | end). 89 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/http_handler.erl: -------------------------------------------------------------------------------- 1 | -module(http_handler). 2 | -export([init/3, init/2, handle/2, terminate/3, doit/1]). 3 | init(_Type, Req, _Opts) -> {ok, Req, no_state}. 4 | init(Req0, Opts) -> 5 | handle(Req0, Opts). 6 | terminate(_Reason, _Req, _State) -> ok. 7 | handle(Req, State) -> 8 | %{ok, Data0, Req2} = cowboy_req:body(Req), 9 | {ok, Data0, _Req2} = cowboy_req:read_body(Req), 10 | %{{IP, _}, Req3} = cowboy_req:peer(Req2), 11 | {IP, _} = cowboy_req:peer(Req), 12 | %io:fwrite("http handler got message: "), 13 | %io:fwrite(Data0), 14 | %io:fwrite("\n"), 15 | E = case bad_work:check(IP) of 16 | bad -> 17 | io:fwrite("ignore bad work\n"), 18 | packer:pack({ok, 0}); 19 | ok -> 20 | Data1 = jiffy:decode(Data0), 21 | Data2 = case Data1 of 22 | [<<"mining_data">>, PubkeyWithWorkerID] -> 23 | %{Pubkey, WorkerID} = pub_split(PubkeyWithWorkerID), 24 | [<<"mining_data">>, 0]; 25 | [<<"work">>, NonceAA, PubkeyWithWorkerID] -> 26 | {Pubkey, _WorkerID} = pub_split(PubkeyWithWorkerID), 27 | [<<"work">>, NonceAA, Pubkey]; 28 | _ -> Data1 29 | end, 30 | %io:fwrite("data 0 is "), 31 | %io:fwrite(Data0), 32 | %io:fwrite("\n"), 33 | Data = packer:unpack_helper(Data2), 34 | %Data = packer:unpack(Data0), 35 | D0 = case Data of 36 | {work, Nonce, Pubkey22} -> 37 | mining_pool_server:receive_work(Nonce, Pubkey22, IP); 38 | _ -> doit(Data) 39 | end, 40 | packer:pack(D0) 41 | end, 42 | Headers = #{ <<"content-type">> => <<"application/octet-stream">>, 43 | <<"Access-Control-Allow-Origin">> => <<"*">>}, 44 | Req4 = cowboy_req:reply(200, Headers, E, Req), 45 | {ok, Req4, State}. 46 | 47 | doit({account, 2}) -> 48 | D = accounts:check(),%duplicating the database here is no good. It will be slow if there are too many accounts. 49 | {ok, dict:fetch(total, D)}; 50 | doit({account, Pubkey}) -> 51 | accounts:balance(Pubkey); 52 | doit({spend, SR}) -> 53 | spawn( 54 | fun() ->R = element(2, SR), 55 | {27, Pubkey, Height} = R, 56 | {ok, NodeHeight} = packer:unpack(talker:talk_helper({height}, config:full_node(), 10)), 57 | true = NodeHeight < Height + 3, 58 | true = NodeHeight > Height - 1, 59 | Sig = element(3, SR), 60 | true = sign:verify_sig(R, Sig, Pubkey), 61 | accounts:pay_veo(Pubkey) 62 | end), 63 | {ok, 0}; 64 | doit({height}) -> 65 | {ok, NodeHeight} = packer:unpack(talker:talk_helper({height}, config:full_node(), 10)), 66 | {ok, NodeHeight}; 67 | doit({mining_data, _}) -> 68 | {ok, [Hash, Nonce, Diff]} = 69 | mining_pool_server:problem_api_mimic(), 70 | {ok, [Hash, Diff, Diff]}; 71 | doit({mining_data}) -> 72 | mining_pool_server:problem_api_mimic(); 73 | doit({accounts}) -> 74 | {ok, hashpower_leaders:read()}. 75 | %doit({work, Nonce, Pubkey}) -> 76 | %io:fwrite("attempted work \n"), 77 | % mining_pool_server:receive_work(Nonce, Pubkey, IP). 78 | 79 | 80 | pub_split(<>) -> 81 | {<>, 0}; 82 | pub_split(PubkeyWithWorkerID) -> 83 | <> = 84 | PubkeyWithWorkerID, 85 | {<>, base64:encode(ID)}. 86 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/mining_pool_server.erl: -------------------------------------------------------------------------------- 1 | -module(mining_pool_server). 2 | -behaviour(gen_server). 3 | -export([start_link/0,code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2, 4 | start_cron/0, problem_api_mimic/0, receive_work/3, 5 | check_solution/1, found_solution/1]). 6 | -record(data, {hash, nonce, diff, time, solutions = dict:new()}). 7 | %init(ok) -> {ok, new_problem_internal()}. 8 | init(ok) -> {ok, new_problem_internal()}. 9 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 10 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 11 | terminate(_, _) -> io:format("died!"), ok. 12 | handle_info(_, X) -> {noreply, X}. 13 | handle_cast(new_problem_cron, Y) -> 14 | N = time_now(), 15 | T = Y#data.time, 16 | RP = config:refresh_period(), 17 | X = if 18 | (((N-T) > RP) or ((N-T) < 0)) -> 19 | new_problem_internal(); 20 | true -> Y 21 | end, 22 | {noreply, X}; 23 | handle_cast({found_solution, S}, Y) -> 24 | D2 = dict:store(S, 0, Y#data.solutions), 25 | Y2 = Y#data{solutions = D2}, 26 | {noreply, Y2}; 27 | handle_cast(_, X) -> {noreply, X}. 28 | handle_call({check_solution, S}, _, Y) -> 29 | X = case dict:find(S, Y#data.solutions) of 30 | error -> true; 31 | {ok, _} -> false 32 | end, 33 | {reply, X, Y}; 34 | handle_call(problem, _From, X) -> 35 | {reply, X, X}; 36 | handle_call(new_problem, _From, _Y) -> 37 | X = new_problem_internal(), 38 | {reply, X, X}; 39 | handle_call(_, _From, X) -> {reply, X, X}. 40 | time_now() -> 41 | element(2, now()). 42 | new_problem_internal() -> 43 | Data = {mining_data}, 44 | case talker:talk_helper(Data, config:full_node(), 10000) of 45 | ok -> new_problem_internal(); 46 | X -> 47 | {ok, [F, S, Third]} = packer:unpack(X), 48 | #data{hash = F, nonce = S, diff = Third, time = time_now()} 49 | end. 50 | problem() -> gen_server:call(?MODULE, problem). 51 | problem_api_mimic() -> 52 | %looks the same as amoveo api. 53 | %io:fwrite("give them a problem\n"), 54 | D = problem(), 55 | Hash = D#data.hash,% = block:hash(header#header{nonce = <<0:256>>}) 56 | Nonce = D#data.nonce, 57 | Diff = easy_diff(D#data.diff), 58 | {ok, [Hash, Nonce, Diff]}. 59 | new_problem() -> gen_server:call(?MODULE, new_problem). 60 | start_cron() -> 61 | spawn(fun() -> 62 | start_cron2() 63 | end). 64 | 65 | start_cron2() -> 66 | %This checks every 0.1 seconds, to see if it is time to get a new problem. 67 | %We get a new problem every ?RefreshPeriod. 68 | timer:sleep(500), 69 | gen_server:cast(?MODULE, new_problem_cron), 70 | start_cron2(). 71 | %receive_work(<>, Pubkey, IP) -> 72 | receive_work(Nonce0, Pubkey, IP) -> 73 | %io:fwrite("mining pool server receive work\n"), 74 | %Pubkey = base64:decode(Pubkey0), 75 | Nonce = case Nonce0 of 76 | <> -> X; 77 | <> -> X 78 | end, 79 | D = problem(), 80 | H = D#data.hash, 81 | Diff = D#data.diff, 82 | EasyDiff = easy_diff(D#data.diff), 83 | %io:fwrite(packer:pack({recent_work, H, Diff, Nonce})), 84 | %io:fwrite("\n"), 85 | Y = <>, 86 | I = pow:hash2integer(hash:doit(Y), 1), 87 | if 88 | I > EasyDiff -> 89 | true = check_solution(Nonce), 90 | found_solution(Nonce), 91 | io:fwrite("found share\n"), 92 | io:fwrite("nonce: "), 93 | io:fwrite(integer_to_list(Nonce)), 94 | io:fwrite("\n"), 95 | io:fwrite("pub: "), 96 | %io:fwrite(Pubkey), 97 | accounts:give_share(Pubkey), 98 | reward_tracker:did_work(Pubkey, H), 99 | if 100 | I > Diff -> 101 | %io:fwrite("found block 000\n"), 102 | found_block(<>), 103 | io:fwrite("found block\n"), 104 | %io:fwrite(packer:pack({recent_work, H, Diff, Nonce, Pubkey})), 105 | %io:fwrite("\n"), 106 | accounts:save(), 107 | {ok, "found block"}; 108 | true -> 109 | {ok, "found work"} 110 | end; 111 | true -> 112 | io:fwrite("bad work received\n"), 113 | bad_work:received(IP), 114 | {ok, "invalid work"} 115 | end. 116 | found_block(<>) -> 117 | %BinNonce = base64:encode(<>), 118 | Data = {work, <>, 0}, 119 | _X = talker:talk_helper(Data, config:full_node(), 10),%spend 8 seconds checking 5 times per second if we can start mining again. 120 | %accounts:got_reward(), 121 | %accounts:pay_veo(), 122 | spawn(fun() -> 123 | timer:sleep(1000), 124 | new_problem() 125 | end), 126 | ok. 127 | easy_diff(D) -> 128 | max(257, D - (256 * config:share_block_ratio())). 129 | check_solution(N) -> 130 | gen_server:call(?MODULE, {check_solution, N}). 131 | found_solution(N) -> 132 | gen_server:cast(?MODULE, {found_solution, N}). 133 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/reward_tracker.erl: -------------------------------------------------------------------------------- 1 | -module(reward_tracker). 2 | -behaviour(gen_server). 3 | -export([start_link/0,code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2, 4 | history_accumulator/0, 5 | did_work/2, new_block/1, save/0]). 6 | 7 | -record(r, {pub, hash, paid = false}). 8 | -record(h, {rs = []}). 9 | 10 | -define(File, "reward_tracker.db"). 11 | 12 | initial_state() -> dict:new(). 13 | 14 | init(ok) -> 15 | A = case file:read_file(?File) of 16 | {error, enoent} -> initial_state(); 17 | {ok, B} -> 18 | case B of 19 | "" -> initial_state(); 20 | _ -> D = binary_to_term(B), 21 | D 22 | end 23 | end, 24 | {ok, A}. 25 | 26 | 27 | save_internal(X) -> file:write_file(?File, term_to_binary(X)). 28 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 29 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 30 | terminate(_, X) -> 31 | io:format("reward tracker died!"), 32 | save_internal(X), 33 | ok. 34 | handle_info(_, X) -> {noreply, X}. 35 | handle_cast({did_work, Pub, Hash}, X) -> 36 | io:fwrite("reward tracker did work\n"), 37 | %this hash is not sha256(block_hash) = sha256(sha256(serialized_header)) 38 | %it is data.hash, which is the third thing from from full node's {mining_data} = hash:doit(block:hash(Block)) 39 | V1 = case dict:find(Hash, X) of 40 | error -> #h{}; 41 | {ok, V = #h{}} -> V 42 | end, 43 | V2 = add(Pub, Hash, V1), 44 | X2 = dict:store(Hash, V2, X), 45 | {noreply, X2}; 46 | handle_cast({new_block, Hash}, X) -> 47 | X2 = case dict:find(Hash, X) of 48 | error -> 49 | %Io:fwrite("we did not find that block\n"), 50 | X; 51 | {ok, H = #h{rs = Rs}} -> 52 | %io:fwrite("block was mined by us\n"), 53 | %io:fwrite(base64:encode(Hash)), 54 | %io:fwrite("\n"), 55 | Rs2 = pay_if_exists(Rs, Hash), 56 | Hs2 = H#h{rs = Rs2}, 57 | dict:store(Hash, Hs2, X); 58 | _ -> io:fwrite("rewards tracker impossible error\n") 59 | 60 | end, 61 | {noreply, X2}; 62 | handle_cast(save, X) -> 63 | save_internal(X), 64 | {noreply, X}; 65 | handle_cast(_, X) -> {noreply, X}. 66 | handle_call(_, _From, X) -> {reply, X, X}. 67 | 68 | pay_if_exists([], _) -> []; 69 | pay_if_exists([R = #r{pub = Pub, hash = Hash, paid = false}|T], Hash) -> 70 | pay(Pub), 71 | [R#r{paid = true}|T]; 72 | pay_if_exists([H|T], Hash) -> 73 | R = pay_if_exists(T, Hash), 74 | [R|pay_if_exists(T, Hash)]. 75 | 76 | add(Pub, Hash, H) -> 77 | B = is_in(Hash, H#h.rs), 78 | if 79 | B -> H; 80 | true -> 81 | R = H#h.rs, 82 | H#h{rs = [#r{hash = Hash, pub = Pub}|R]} 83 | end. 84 | 85 | pay(Pubkey) -> 86 | io:fwrite("reward tracker, make payment\n"), 87 | BlockReward = 10382390, 88 | V = BlockReward * 5 div 6, 89 | {ok, Height} = packer:unpack(talker:talk_helper({height, 1}, config:full_node(), 3)), 90 | A = V - config:tx_fee(), 91 | S = binary_to_list(base64:encode(Pubkey)) ++ " amount: " ++ integer_to_list(A) ++ " height: " ++ integer_to_list(Height) ++ "\n", 92 | file:write_file(config:spend_log_file(), S, [append]), 93 | Msg = {spend, Pubkey, A}, 94 | talker:talk_helper(Msg, config:full_node(), 10). 95 | 96 | 97 | is_in(Hash, []) -> false; 98 | is_in(Hash, [R = #r{hash = Hash}|_]) -> true; 99 | is_in(Hash, [_|T]) -> 100 | is_in(Hash, T). 101 | 102 | did_work(Pub, Hash) -> 103 | gen_server:cast(?MODULE, {did_work, Pub, Hash}). 104 | 105 | new_block(Hash) -> 106 | %io:fwrite("calling reward_tracker:new_block/1\n"), 107 | gen_server:cast(?MODULE, {new_block, Hash}). 108 | 109 | save() -> 110 | gen_server:cast(?MODULE, save). 111 | 112 | reward_accumulator(End, End, DB) -> DB; 113 | reward_accumulator(Start, End, DB) -> 114 | {ok, Hash} = packer:unpack(talker:talk_helper({block_hash, 2, Start}, config:full_node(), 3)),%not the hash we want. we want the hash of the header if the nonce is set to zero. 115 | H2 = hash:doit(Hash), 116 | reward_accumulator(Start +1, End, dict:store(H2, 1, DB)). 117 | 118 | history_accumulator() -> 119 | DB = gen_server:call(?MODULE, ok), 120 | Keys = dict:fetch_keys(DB), 121 | RDB = reward_accumulator(329000, 330570, dict:new()), 122 | X = lists:map(fun(K) -> 123 | {ok, #h{rs = RS}} = dict:find(K, DB), 124 | %lists:map(fun(#r{pub = P, hash = Hash, paid = Paid}) -> 125 | lists:map(fun(R) -> 126 | case R of 127 | #r{pub = P, hash = Hash, paid = false} -> 128 | case dict:find(Hash, RDB) of 129 | {ok, 1} -> {P, Hash}; 130 | _ -> [] 131 | end; 132 | #r{} -> []; 133 | _ -> io:fwrite(R) 134 | end 135 | end, RS) 136 | end, Keys), 137 | DB2 = history_accumulator2(X, dict:new()), 138 | Accs = dict:fetch_keys(DB2), 139 | lists:map(fun(A) -> 140 | {base64:encode(A), dict:fetch(A, DB2)} 141 | end, Accs). 142 | history_accumulator2([], D) -> D; 143 | history_accumulator2([[H|T1]|T2], D) -> 144 | D2 = history_accumulator2([H|T1], D), 145 | history_accumulator2(T2, D2); 146 | history_accumulator2([ok|T], D) -> 147 | history_accumulator2(T, D); 148 | history_accumulator2([[]|T], D) -> 149 | history_accumulator2(T, D); 150 | history_accumulator2([{Pub, Hash}|T], D) -> 151 | Dict2 = case dict:find(Pub, D) of 152 | error -> 153 | dict:store(Pub, 1, D); 154 | {ok, N} -> dict:store(Pub, N+1, D) 155 | end, 156 | history_accumulator2(T, Dict2). 157 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/rewards.erl: -------------------------------------------------------------------------------- 1 | %This module keeps track of the current block height. 2 | %It checks if we found a block CONFIRMATION blocks ago, and if we did, it pays out a reward like this: 3 | %accounts:got_reward(), 4 | %accounts:pay_veo(), 5 | -module(rewards). 6 | -behaviour(gen_server). 7 | -export([start_link/0,code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2, 8 | check/0, update/1]). 9 | -define(File, "rewards.db"). 10 | initial_state() -> 0. 11 | init(ok) -> 12 | A = case file:read_file(?File) of 13 | {error, enoent} -> initial_state(); 14 | {ok, B} -> 15 | case B of 16 | "" -> initial_state(); 17 | _ -> binary_to_term(B) 18 | end 19 | end, 20 | spawn(fun() -> rewards_cron() end), 21 | {ok, A}. 22 | save(X) -> file:write_file(?File, term_to_binary(X)). 23 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 24 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 25 | terminate(_, _) -> io:format("died!"), ok. 26 | handle_info(_, X) -> {noreply, X}. 27 | handle_cast({update, H}, X) -> 28 | X2 = max(H, X), 29 | save(X2), 30 | {noreply, X2}; 31 | handle_cast(_, X) -> {noreply, X}. 32 | handle_call(check, _From, X) -> {reply, X, X}; 33 | handle_call(_, _From, X) -> {reply, X, X}. 34 | 35 | check() -> gen_server:call(?MODULE, check). 36 | update(H) -> gen_server:cast(?MODULE, {update, H}). 37 | 38 | rewards_cron() -> 39 | %io:fwrite("rewards cron\n"), 40 | timer:sleep(2000), 41 | spawn(fun() -> rewards_pusher:new_height() end), 42 | rewards_cron(). 43 | 44 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/rewards_pusher.erl: -------------------------------------------------------------------------------- 1 | -module(rewards_pusher). 2 | -behaviour(gen_server). 3 | -export([start_link/0,code_change/3,handle_call/3,handle_cast/2,handle_info/2,init/1,terminate/2]). 4 | -export([new_height/0, pay_rewards2/2]). 5 | 6 | init(ok) -> {ok, []}. 7 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 8 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 9 | terminate(_, _) -> io:format("died!"), ok. 10 | handle_info(_, X) -> {noreply, X}. 11 | handle_cast(new_height, X) -> 12 | new_height_internal(), 13 | {noreply, X}; 14 | handle_cast(_, X) -> {noreply, X}. 15 | handle_call(_, _From, X) -> {reply, X, X}. 16 | new_height() -> 17 | gen_server:cast(?MODULE, new_height). 18 | new_height_internal() -> 19 | {ok, H} = packer:unpack(talker:talk_helper({height, 1}, config:full_node(), 3)), 20 | 21 | Confs = config:confirmations(), 22 | H2 = H - (2*Confs), 23 | %Old = rewards:check(), 24 | if 25 | (H2 > 0) -> 26 | %{ok, ServerPub} = packer:unpack(talker:talk_helper({pubkey}, config:full_node(), 3)), 27 | %{ok, Blocks} = packer:unpack(talker:talk_helper({blocks, 4, H2, H2 + Confs}}, config:full_node(), 3)), 28 | %blocks is a list of blocks. H2 is the starting height, H is the ending height of the range. 29 | %io:fwrite("rewards_pusher, calling pay_rewards2\n"), 30 | pay_rewards2(H2, H2+Confs); 31 | %pay_rewards(Blocks, ServerPub), 32 | %rewards:update(H2); 33 | true -> ok 34 | end. 35 | pay_rewards(B, _ServerPub) when is_binary(B) -> 36 | io:fwrite("rewards pusher got a binary instead of a list of blocks."), 37 | ok; 38 | pay_rewards([], _ServerPub) -> 39 | accounts:pay_veo(); 40 | pay_rewards([H|T], ServerPub) -> 41 | Txs = element(11, H), 42 | case Txs of 43 | [] -> io:fwrite("block 0\n"); 44 | _ -> 45 | CB = hd(Txs), 46 | Pub = base64:encode(element(2, CB)), 47 | if 48 | Pub == ServerPub -> accounts:got_reward(); 49 | true -> ok 50 | end 51 | end, 52 | pay_rewards(T, ServerPub). 53 | 54 | pay_rewards2(A, B) -> 55 | {ok, ServerPub} = packer:unpack(talker:talk_helper({pubkey}, config:full_node(), 3)), 56 | pay_rewards2(A, B, ServerPub). 57 | 58 | pay_rewards2(A, B, _ServerPub) when A > B -> ok; 59 | pay_rewards2(Start, End, ServerPub) -> 60 | %{ok, Hash} = packer:unpack(talker:talk_helper({block_hash, Start}, config:full_node(), 3)),%not the hash we want. we want the hash of the header if the nonce is set to zero. 61 | {ok, Hash} = packer:unpack(talker:talk_helper({block_hash, 2, Start}, config:full_node(), 3)),%not the hash we want. we want the hash of the header if the nonce is set to zero. 62 | <<_:256>> = Hash, 63 | if 64 | true -> ok; 65 | ((Start rem 20) == 0) -> io:fwrite("pay rewards height: "), 66 | io:fwrite(integer_to_list(Start)), 67 | io:fwrite(base64:encode(Hash)), 68 | io:fwrite("\n"); 69 | true -> ok 70 | end, 71 | %io:fwrite("rewards pusher: pay_rewards2 "), 72 | %io:fwrite(integer_to_list(Start)), 73 | %io:fwrite("\n"), 74 | %io:fwrite("hash is: "), 75 | %io:fwrite(base64:encode(Hash)), 76 | %io:fwrite("\n"), 77 | reward_tracker:new_block(hash:doit(Hash)), 78 | pay_rewards2(Start + 1, End, ServerPub). 79 | -------------------------------------------------------------------------------- /apps/amoveo_mining_pool/src/talker.erl: -------------------------------------------------------------------------------- 1 | -module(talker). 2 | -export([talk_helper/3]). 3 | 4 | 5 | talk_helper2(Data, Peer) -> 6 | D2 = iolist_to_binary(packer:pack(Data)), 7 | httpc:request(post, {Peer, [], "application/octet-stream", D2}, [{timeout, 3000}], []). 8 | talk_helper(Data, Peer, N) -> 9 | if 10 | N == 0 -> 11 | io:fwrite("cannot connect to server\n"), 12 | io:fwrite(packer:pack(Peer)), 13 | io:fwrite(packer:pack(Data)), 14 | %timer:sleep(2000), 15 | %talk_helper(Data, Peer, 1); 16 | ok; 17 | %1=2; 18 | true -> 19 | case talk_helper2(Data, Peer) of 20 | {ok, {_Status, _Headers, []}} -> 21 | %io:fwrite("first failure mode \n"), 22 | timer:sleep(100), 23 | talk_helper(Data, Peer, N - 1); 24 | {ok, {_, _, R}} -> R; 25 | {error, timeout} -> 26 | io:fwrite(packer:pack(Data)), 27 | io:fwrite("timeout error\n"), 28 | timer:sleep(500), 29 | talk_helper(Data, Peer, N - 1); 30 | X -> 31 | io:fwrite(packer:pack({Peer, X})), 32 | io:fwrite("\nYou need to turn on and sync your Amoveo node before you can mine. You can get it here: https://github.com/zack-bitcoin/amoveo \n"), 33 | timer:sleep(1000), 34 | talk_helper(Data, Peer, N - 1) 35 | % 1=2 36 | end 37 | end. 38 | -------------------------------------------------------------------------------- /attach.sh: -------------------------------------------------------------------------------- 1 | ./_build/prod/rel/amoveo_mining_pool/bin/amoveo_mining_pool attach 2 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | rm _build/prod/rel/amoveo_mining_pool/*.db 2 | -------------------------------------------------------------------------------- /config/sys.config: -------------------------------------------------------------------------------- 1 | [ 2 | { amoveo_mining_pool, [ 3 | {test, []}, 4 | {port, 8085} 5 | ]} 6 | ]. 7 | -------------------------------------------------------------------------------- /config/sys.config~: -------------------------------------------------------------------------------- 1 | [ 2 | { amoveo_mining_pool, []} 3 | ]. 4 | -------------------------------------------------------------------------------- /config/vm.args: -------------------------------------------------------------------------------- 1 | -sname amoveo_mining_pool 2 | 3 | 4 | +K true 5 | +A30 6 | -------------------------------------------------------------------------------- /docs/todo.md: -------------------------------------------------------------------------------- 1 | * there was an update called "don't crash from bad work". This change may have made it impossible for us to find blocks somehow. 2 | 3 | 4 | * we need more explanations about shares and total-shares. 5 | - shares get converted to veo 6 | - block rewards are distributed based on how many shares you have. 7 | 8 | * we should probably display the pubkey for the mining pool. 9 | 10 | Right now the miner only rewards a worker who finds a block. 11 | 12 | * mining pool should say the fee size, the number of active miners, last block made today, blocks made in last 24 hours, total veo distributed, current block height? 13 | 14 | Instead we want to give out rewards that are 1/100th as big, and give them out 100 times per block mined. 15 | The risk is that multiple people could send us the same work more than once. 16 | To avoid this, we should remember every solution (or a few bytes of every solution), so that the same solution cannot be given more than once. 17 | 18 | 19 | Maybe we should be able to turn down the frequency at which particular addresses get paid. 20 | 21 | 22 | clarify that shares is not the total shares mined. it is your current outstanding balance of shares. 23 | 24 | Clarify that veo and shares are 2 different balances. -------------------------------------------------------------------------------- /js/BigInteger.js: -------------------------------------------------------------------------------- 1 | var bigInt = (function (undefined) { 2 | "use strict"; 3 | 4 | var BASE = 1e7, 5 | LOG_BASE = 7, 6 | MAX_INT = 9007199254740992, 7 | MAX_INT_ARR = smallToArray(MAX_INT), 8 | LOG_MAX_INT = Math.log(MAX_INT); 9 | 10 | function Integer(v, radix) { 11 | if (typeof v === "undefined") return Integer[0]; 12 | if (typeof radix !== "undefined") return +radix === 10 ? parseValue(v) : parseBase(v, radix); 13 | return parseValue(v); 14 | } 15 | 16 | function BigInteger(value, sign) { 17 | this.value = value; 18 | this.sign = sign; 19 | this.isSmall = false; 20 | } 21 | BigInteger.prototype = Object.create(Integer.prototype); 22 | 23 | function SmallInteger(value) { 24 | this.value = value; 25 | this.sign = value < 0; 26 | this.isSmall = true; 27 | } 28 | SmallInteger.prototype = Object.create(Integer.prototype); 29 | 30 | function isPrecise(n) { 31 | return -MAX_INT < n && n < MAX_INT; 32 | } 33 | 34 | function smallToArray(n) { // For performance reasons doesn't reference BASE, need to change this function if BASE changes 35 | if (n < 1e7) 36 | return [n]; 37 | if (n < 1e14) 38 | return [n % 1e7, Math.floor(n / 1e7)]; 39 | return [n % 1e7, Math.floor(n / 1e7) % 1e7, Math.floor(n / 1e14)]; 40 | } 41 | 42 | function arrayToSmall(arr) { // If BASE changes this function may need to change 43 | trim(arr); 44 | var length = arr.length; 45 | if (length < 4 && compareAbs(arr, MAX_INT_ARR) < 0) { 46 | switch (length) { 47 | case 0: return 0; 48 | case 1: return arr[0]; 49 | case 2: return arr[0] + arr[1] * BASE; 50 | default: return arr[0] + (arr[1] + arr[2] * BASE) * BASE; 51 | } 52 | } 53 | return arr; 54 | } 55 | 56 | function trim(v) { 57 | var i = v.length; 58 | while (v[--i] === 0); 59 | v.length = i + 1; 60 | } 61 | 62 | function createArray(length) { // function shamelessly stolen from Yaffle's library https://github.com/Yaffle/BigInteger 63 | var x = new Array(length); 64 | var i = -1; 65 | while (++i < length) { 66 | x[i] = 0; 67 | } 68 | return x; 69 | } 70 | 71 | function truncate(n) { 72 | if (n > 0) return Math.floor(n); 73 | return Math.ceil(n); 74 | } 75 | 76 | function add(a, b) { // assumes a and b are arrays with a.length >= b.length 77 | var l_a = a.length, 78 | l_b = b.length, 79 | r = new Array(l_a), 80 | carry = 0, 81 | base = BASE, 82 | sum, i; 83 | for (i = 0; i < l_b; i++) { 84 | sum = a[i] + b[i] + carry; 85 | carry = sum >= base ? 1 : 0; 86 | r[i] = sum - carry * base; 87 | } 88 | while (i < l_a) { 89 | sum = a[i] + carry; 90 | carry = sum === base ? 1 : 0; 91 | r[i++] = sum - carry * base; 92 | } 93 | if (carry > 0) r.push(carry); 94 | return r; 95 | } 96 | 97 | function addAny(a, b) { 98 | if (a.length >= b.length) return add(a, b); 99 | return add(b, a); 100 | } 101 | 102 | function addSmall(a, carry) { // assumes a is array, carry is number with 0 <= carry < MAX_INT 103 | var l = a.length, 104 | r = new Array(l), 105 | base = BASE, 106 | sum, i; 107 | for (i = 0; i < l; i++) { 108 | sum = a[i] - base + carry; 109 | carry = Math.floor(sum / base); 110 | r[i] = sum - carry * base; 111 | carry += 1; 112 | } 113 | while (carry > 0) { 114 | r[i++] = carry % base; 115 | carry = Math.floor(carry / base); 116 | } 117 | return r; 118 | } 119 | 120 | BigInteger.prototype.add = function (v) { 121 | var n = parseValue(v); 122 | if (this.sign !== n.sign) { 123 | return this.subtract(n.negate()); 124 | } 125 | var a = this.value, b = n.value; 126 | if (n.isSmall) { 127 | return new BigInteger(addSmall(a, Math.abs(b)), this.sign); 128 | } 129 | return new BigInteger(addAny(a, b), this.sign); 130 | }; 131 | BigInteger.prototype.plus = BigInteger.prototype.add; 132 | 133 | SmallInteger.prototype.add = function (v) { 134 | var n = parseValue(v); 135 | var a = this.value; 136 | if (a < 0 !== n.sign) { 137 | return this.subtract(n.negate()); 138 | } 139 | var b = n.value; 140 | if (n.isSmall) { 141 | if (isPrecise(a + b)) return new SmallInteger(a + b); 142 | b = smallToArray(Math.abs(b)); 143 | } 144 | return new BigInteger(addSmall(b, Math.abs(a)), a < 0); 145 | }; 146 | SmallInteger.prototype.plus = SmallInteger.prototype.add; 147 | 148 | function subtract(a, b) { // assumes a and b are arrays with a >= b 149 | var a_l = a.length, 150 | b_l = b.length, 151 | r = new Array(a_l), 152 | borrow = 0, 153 | base = BASE, 154 | i, difference; 155 | for (i = 0; i < b_l; i++) { 156 | difference = a[i] - borrow - b[i]; 157 | if (difference < 0) { 158 | difference += base; 159 | borrow = 1; 160 | } else borrow = 0; 161 | r[i] = difference; 162 | } 163 | for (i = b_l; i < a_l; i++) { 164 | difference = a[i] - borrow; 165 | if (difference < 0) difference += base; 166 | else { 167 | r[i++] = difference; 168 | break; 169 | } 170 | r[i] = difference; 171 | } 172 | for (; i < a_l; i++) { 173 | r[i] = a[i]; 174 | } 175 | trim(r); 176 | return r; 177 | } 178 | 179 | function subtractAny(a, b, sign) { 180 | var value; 181 | if (compareAbs(a, b) >= 0) { 182 | value = subtract(a, b); 183 | } else { 184 | value = subtract(b, a); 185 | sign = !sign; 186 | } 187 | value = arrayToSmall(value); 188 | if (typeof value === "number") { 189 | if (sign) value = -value; 190 | return new SmallInteger(value); 191 | } 192 | return new BigInteger(value, sign); 193 | } 194 | 195 | function subtractSmall(a, b, sign) { // assumes a is array, b is number with 0 <= b < MAX_INT 196 | var l = a.length, 197 | r = new Array(l), 198 | carry = -b, 199 | base = BASE, 200 | i, difference; 201 | for (i = 0; i < l; i++) { 202 | difference = a[i] + carry; 203 | carry = Math.floor(difference / base); 204 | difference %= base; 205 | r[i] = difference < 0 ? difference + base : difference; 206 | } 207 | r = arrayToSmall(r); 208 | if (typeof r === "number") { 209 | if (sign) r = -r; 210 | return new SmallInteger(r); 211 | } return new BigInteger(r, sign); 212 | } 213 | 214 | BigInteger.prototype.subtract = function (v) { 215 | var n = parseValue(v); 216 | if (this.sign !== n.sign) { 217 | return this.add(n.negate()); 218 | } 219 | var a = this.value, b = n.value; 220 | if (n.isSmall) 221 | return subtractSmall(a, Math.abs(b), this.sign); 222 | return subtractAny(a, b, this.sign); 223 | }; 224 | BigInteger.prototype.minus = BigInteger.prototype.subtract; 225 | 226 | SmallInteger.prototype.subtract = function (v) { 227 | var n = parseValue(v); 228 | var a = this.value; 229 | if (a < 0 !== n.sign) { 230 | return this.add(n.negate()); 231 | } 232 | var b = n.value; 233 | if (n.isSmall) { 234 | return new SmallInteger(a - b); 235 | } 236 | return subtractSmall(b, Math.abs(a), a >= 0); 237 | }; 238 | SmallInteger.prototype.minus = SmallInteger.prototype.subtract; 239 | 240 | BigInteger.prototype.negate = function () { 241 | return new BigInteger(this.value, !this.sign); 242 | }; 243 | SmallInteger.prototype.negate = function () { 244 | var sign = this.sign; 245 | var small = new SmallInteger(-this.value); 246 | small.sign = !sign; 247 | return small; 248 | }; 249 | 250 | BigInteger.prototype.abs = function () { 251 | return new BigInteger(this.value, false); 252 | }; 253 | SmallInteger.prototype.abs = function () { 254 | return new SmallInteger(Math.abs(this.value)); 255 | }; 256 | 257 | function multiplyLong(a, b) { 258 | var a_l = a.length, 259 | b_l = b.length, 260 | l = a_l + b_l, 261 | r = createArray(l), 262 | base = BASE, 263 | product, carry, i, a_i, b_j; 264 | for (i = 0; i < a_l; ++i) { 265 | a_i = a[i]; 266 | for (var j = 0; j < b_l; ++j) { 267 | b_j = b[j]; 268 | product = a_i * b_j + r[i + j]; 269 | carry = Math.floor(product / base); 270 | r[i + j] = product - carry * base; 271 | r[i + j + 1] += carry; 272 | } 273 | } 274 | trim(r); 275 | return r; 276 | } 277 | 278 | function multiplySmall(a, b) { // assumes a is array, b is number with |b| < BASE 279 | var l = a.length, 280 | r = new Array(l), 281 | base = BASE, 282 | carry = 0, 283 | product, i; 284 | for (i = 0; i < l; i++) { 285 | product = a[i] * b + carry; 286 | carry = Math.floor(product / base); 287 | r[i] = product - carry * base; 288 | } 289 | while (carry > 0) { 290 | r[i++] = carry % base; 291 | carry = Math.floor(carry / base); 292 | } 293 | return r; 294 | } 295 | 296 | function shiftLeft(x, n) { 297 | var r = []; 298 | while (n-- > 0) r.push(0); 299 | return r.concat(x); 300 | } 301 | 302 | function multiplyKaratsuba(x, y) { 303 | var n = Math.max(x.length, y.length); 304 | 305 | if (n <= 30) return multiplyLong(x, y); 306 | n = Math.ceil(n / 2); 307 | 308 | var b = x.slice(n), 309 | a = x.slice(0, n), 310 | d = y.slice(n), 311 | c = y.slice(0, n); 312 | 313 | var ac = multiplyKaratsuba(a, c), 314 | bd = multiplyKaratsuba(b, d), 315 | abcd = multiplyKaratsuba(addAny(a, b), addAny(c, d)); 316 | 317 | var product = addAny(addAny(ac, shiftLeft(subtract(subtract(abcd, ac), bd), n)), shiftLeft(bd, 2 * n)); 318 | trim(product); 319 | return product; 320 | } 321 | 322 | // The following function is derived from a surface fit of a graph plotting the performance difference 323 | // between long multiplication and karatsuba multiplication versus the lengths of the two arrays. 324 | function useKaratsuba(l1, l2) { 325 | return -0.012 * l1 - 0.012 * l2 + 0.000015 * l1 * l2 > 0; 326 | } 327 | 328 | BigInteger.prototype.multiply = function (v) { 329 | var n = parseValue(v), 330 | a = this.value, b = n.value, 331 | sign = this.sign !== n.sign, 332 | abs; 333 | if (n.isSmall) { 334 | if (b === 0) return Integer[0]; 335 | if (b === 1) return this; 336 | if (b === -1) return this.negate(); 337 | abs = Math.abs(b); 338 | if (abs < BASE) { 339 | return new BigInteger(multiplySmall(a, abs), sign); 340 | } 341 | b = smallToArray(abs); 342 | } 343 | if (useKaratsuba(a.length, b.length)) // Karatsuba is only faster for certain array sizes 344 | return new BigInteger(multiplyKaratsuba(a, b), sign); 345 | return new BigInteger(multiplyLong(a, b), sign); 346 | }; 347 | 348 | BigInteger.prototype.times = BigInteger.prototype.multiply; 349 | 350 | function multiplySmallAndArray(a, b, sign) { // a >= 0 351 | if (a < BASE) { 352 | return new BigInteger(multiplySmall(b, a), sign); 353 | } 354 | return new BigInteger(multiplyLong(b, smallToArray(a)), sign); 355 | } 356 | SmallInteger.prototype._multiplyBySmall = function (a) { 357 | if (isPrecise(a.value * this.value)) { 358 | return new SmallInteger(a.value * this.value); 359 | } 360 | return multiplySmallAndArray(Math.abs(a.value), smallToArray(Math.abs(this.value)), this.sign !== a.sign); 361 | }; 362 | BigInteger.prototype._multiplyBySmall = function (a) { 363 | if (a.value === 0) return Integer[0]; 364 | if (a.value === 1) return this; 365 | if (a.value === -1) return this.negate(); 366 | return multiplySmallAndArray(Math.abs(a.value), this.value, this.sign !== a.sign); 367 | }; 368 | SmallInteger.prototype.multiply = function (v) { 369 | return parseValue(v)._multiplyBySmall(this); 370 | }; 371 | SmallInteger.prototype.times = SmallInteger.prototype.multiply; 372 | 373 | function square(a) { 374 | //console.assert(2 * BASE * BASE < MAX_INT); 375 | var l = a.length, 376 | r = createArray(l + l), 377 | base = BASE, 378 | product, carry, i, a_i, a_j; 379 | for (i = 0; i < l; i++) { 380 | a_i = a[i]; 381 | carry = 0 - a_i * a_i; 382 | for (var j = i; j < l; j++) { 383 | a_j = a[j]; 384 | product = 2 * (a_i * a_j) + r[i + j] + carry; 385 | carry = Math.floor(product / base); 386 | r[i + j] = product - carry * base; 387 | } 388 | r[i + l] = carry; 389 | } 390 | trim(r); 391 | return r; 392 | } 393 | 394 | BigInteger.prototype.square = function () { 395 | return new BigInteger(square(this.value), false); 396 | }; 397 | 398 | SmallInteger.prototype.square = function () { 399 | var value = this.value * this.value; 400 | if (isPrecise(value)) return new SmallInteger(value); 401 | return new BigInteger(square(smallToArray(Math.abs(this.value))), false); 402 | }; 403 | 404 | function divMod1(a, b) { // Left over from previous version. Performs faster than divMod2 on smaller input sizes. 405 | var a_l = a.length, 406 | b_l = b.length, 407 | base = BASE, 408 | result = createArray(b.length), 409 | divisorMostSignificantDigit = b[b_l - 1], 410 | // normalization 411 | lambda = Math.ceil(base / (2 * divisorMostSignificantDigit)), 412 | remainder = multiplySmall(a, lambda), 413 | divisor = multiplySmall(b, lambda), 414 | quotientDigit, shift, carry, borrow, i, l, q; 415 | if (remainder.length <= a_l) remainder.push(0); 416 | divisor.push(0); 417 | divisorMostSignificantDigit = divisor[b_l - 1]; 418 | for (shift = a_l - b_l; shift >= 0; shift--) { 419 | quotientDigit = base - 1; 420 | if (remainder[shift + b_l] !== divisorMostSignificantDigit) { 421 | quotientDigit = Math.floor((remainder[shift + b_l] * base + remainder[shift + b_l - 1]) / divisorMostSignificantDigit); 422 | } 423 | // quotientDigit <= base - 1 424 | carry = 0; 425 | borrow = 0; 426 | l = divisor.length; 427 | for (i = 0; i < l; i++) { 428 | carry += quotientDigit * divisor[i]; 429 | q = Math.floor(carry / base); 430 | borrow += remainder[shift + i] - (carry - q * base); 431 | carry = q; 432 | if (borrow < 0) { 433 | remainder[shift + i] = borrow + base; 434 | borrow = -1; 435 | } else { 436 | remainder[shift + i] = borrow; 437 | borrow = 0; 438 | } 439 | } 440 | while (borrow !== 0) { 441 | quotientDigit -= 1; 442 | carry = 0; 443 | for (i = 0; i < l; i++) { 444 | carry += remainder[shift + i] - base + divisor[i]; 445 | if (carry < 0) { 446 | remainder[shift + i] = carry + base; 447 | carry = 0; 448 | } else { 449 | remainder[shift + i] = carry; 450 | carry = 1; 451 | } 452 | } 453 | borrow += carry; 454 | } 455 | result[shift] = quotientDigit; 456 | } 457 | // denormalization 458 | remainder = divModSmall(remainder, lambda)[0]; 459 | return [arrayToSmall(result), arrayToSmall(remainder)]; 460 | } 461 | 462 | function divMod2(a, b) { // Implementation idea shamelessly stolen from Silent Matt's library http://silentmatt.com/biginteger/ 463 | // Performs faster than divMod1 on larger input sizes. 464 | var a_l = a.length, 465 | b_l = b.length, 466 | result = [], 467 | part = [], 468 | base = BASE, 469 | guess, xlen, highx, highy, check; 470 | while (a_l) { 471 | part.unshift(a[--a_l]); 472 | trim(part); 473 | if (compareAbs(part, b) < 0) { 474 | result.push(0); 475 | continue; 476 | } 477 | xlen = part.length; 478 | highx = part[xlen - 1] * base + part[xlen - 2]; 479 | highy = b[b_l - 1] * base + b[b_l - 2]; 480 | if (xlen > b_l) { 481 | highx = (highx + 1) * base; 482 | } 483 | guess = Math.ceil(highx / highy); 484 | do { 485 | check = multiplySmall(b, guess); 486 | if (compareAbs(check, part) <= 0) break; 487 | guess--; 488 | } while (guess); 489 | result.push(guess); 490 | part = subtract(part, check); 491 | } 492 | result.reverse(); 493 | return [arrayToSmall(result), arrayToSmall(part)]; 494 | } 495 | 496 | function divModSmall(value, lambda) { 497 | var length = value.length, 498 | quotient = createArray(length), 499 | base = BASE, 500 | i, q, remainder, divisor; 501 | remainder = 0; 502 | for (i = length - 1; i >= 0; --i) { 503 | divisor = remainder * base + value[i]; 504 | q = truncate(divisor / lambda); 505 | remainder = divisor - q * lambda; 506 | quotient[i] = q | 0; 507 | } 508 | return [quotient, remainder | 0]; 509 | } 510 | 511 | function divModAny(self, v) { 512 | var value, n = parseValue(v); 513 | var a = self.value, b = n.value; 514 | var quotient; 515 | if (b === 0) throw new Error("Cannot divide by zero"); 516 | if (self.isSmall) { 517 | if (n.isSmall) { 518 | return [new SmallInteger(truncate(a / b)), new SmallInteger(a % b)]; 519 | } 520 | return [Integer[0], self]; 521 | } 522 | if (n.isSmall) { 523 | if (b === 1) return [self, Integer[0]]; 524 | if (b == -1) return [self.negate(), Integer[0]]; 525 | var abs = Math.abs(b); 526 | if (abs < BASE) { 527 | value = divModSmall(a, abs); 528 | quotient = arrayToSmall(value[0]); 529 | var remainder = value[1]; 530 | if (self.sign) remainder = -remainder; 531 | if (typeof quotient === "number") { 532 | if (self.sign !== n.sign) quotient = -quotient; 533 | return [new SmallInteger(quotient), new SmallInteger(remainder)]; 534 | } 535 | return [new BigInteger(quotient, self.sign !== n.sign), new SmallInteger(remainder)]; 536 | } 537 | b = smallToArray(abs); 538 | } 539 | var comparison = compareAbs(a, b); 540 | if (comparison === -1) return [Integer[0], self]; 541 | if (comparison === 0) return [Integer[self.sign === n.sign ? 1 : -1], Integer[0]]; 542 | 543 | // divMod1 is faster on smaller input sizes 544 | if (a.length + b.length <= 200) 545 | value = divMod1(a, b); 546 | else value = divMod2(a, b); 547 | 548 | quotient = value[0]; 549 | var qSign = self.sign !== n.sign, 550 | mod = value[1], 551 | mSign = self.sign; 552 | if (typeof quotient === "number") { 553 | if (qSign) quotient = -quotient; 554 | quotient = new SmallInteger(quotient); 555 | } else quotient = new BigInteger(quotient, qSign); 556 | if (typeof mod === "number") { 557 | if (mSign) mod = -mod; 558 | mod = new SmallInteger(mod); 559 | } else mod = new BigInteger(mod, mSign); 560 | return [quotient, mod]; 561 | } 562 | 563 | BigInteger.prototype.divmod = function (v) { 564 | var result = divModAny(this, v); 565 | return { 566 | quotient: result[0], 567 | remainder: result[1] 568 | }; 569 | }; 570 | SmallInteger.prototype.divmod = BigInteger.prototype.divmod; 571 | 572 | BigInteger.prototype.divide = function (v) { 573 | return divModAny(this, v)[0]; 574 | }; 575 | SmallInteger.prototype.over = SmallInteger.prototype.divide = BigInteger.prototype.over = BigInteger.prototype.divide; 576 | 577 | BigInteger.prototype.mod = function (v) { 578 | return divModAny(this, v)[1]; 579 | }; 580 | SmallInteger.prototype.remainder = SmallInteger.prototype.mod = BigInteger.prototype.remainder = BigInteger.prototype.mod; 581 | 582 | BigInteger.prototype.pow = function (v) { 583 | var n = parseValue(v), 584 | a = this.value, 585 | b = n.value, 586 | value, x, y; 587 | if (b === 0) return Integer[1]; 588 | if (a === 0) return Integer[0]; 589 | if (a === 1) return Integer[1]; 590 | if (a === -1) return n.isEven() ? Integer[1] : Integer[-1]; 591 | if (n.sign) { 592 | return Integer[0]; 593 | } 594 | if (!n.isSmall) throw new Error("The exponent " + n.toString() + " is too large."); 595 | if (this.isSmall) { 596 | if (isPrecise(value = Math.pow(a, b))) 597 | return new SmallInteger(truncate(value)); 598 | } 599 | x = this; 600 | y = Integer[1]; 601 | while (true) { 602 | if (b & 1 === 1) { 603 | y = y.times(x); 604 | --b; 605 | } 606 | if (b === 0) break; 607 | b /= 2; 608 | x = x.square(); 609 | } 610 | return y; 611 | }; 612 | SmallInteger.prototype.pow = BigInteger.prototype.pow; 613 | 614 | BigInteger.prototype.modPow = function (exp, mod) { 615 | exp = parseValue(exp); 616 | mod = parseValue(mod); 617 | if (mod.isZero()) throw new Error("Cannot take modPow with modulus 0"); 618 | var r = Integer[1], 619 | base = this.mod(mod); 620 | while (exp.isPositive()) { 621 | if (base.isZero()) return Integer[0]; 622 | if (exp.isOdd()) r = r.multiply(base).mod(mod); 623 | exp = exp.divide(2); 624 | base = base.square().mod(mod); 625 | } 626 | return r; 627 | }; 628 | SmallInteger.prototype.modPow = BigInteger.prototype.modPow; 629 | 630 | function compareAbs(a, b) { 631 | if (a.length !== b.length) { 632 | return a.length > b.length ? 1 : -1; 633 | } 634 | for (var i = a.length - 1; i >= 0; i--) { 635 | if (a[i] !== b[i]) return a[i] > b[i] ? 1 : -1; 636 | } 637 | return 0; 638 | } 639 | 640 | BigInteger.prototype.compareAbs = function (v) { 641 | var n = parseValue(v), 642 | a = this.value, 643 | b = n.value; 644 | if (n.isSmall) return 1; 645 | return compareAbs(a, b); 646 | }; 647 | SmallInteger.prototype.compareAbs = function (v) { 648 | var n = parseValue(v), 649 | a = Math.abs(this.value), 650 | b = n.value; 651 | if (n.isSmall) { 652 | b = Math.abs(b); 653 | return a === b ? 0 : a > b ? 1 : -1; 654 | } 655 | return -1; 656 | }; 657 | 658 | BigInteger.prototype.compare = function (v) { 659 | // See discussion about comparison with Infinity: 660 | // https://github.com/peterolson/BigInteger.js/issues/61 661 | if (v === Infinity) { 662 | return -1; 663 | } 664 | if (v === -Infinity) { 665 | return 1; 666 | } 667 | 668 | var n = parseValue(v), 669 | a = this.value, 670 | b = n.value; 671 | if (this.sign !== n.sign) { 672 | return n.sign ? 1 : -1; 673 | } 674 | if (n.isSmall) { 675 | return this.sign ? -1 : 1; 676 | } 677 | return compareAbs(a, b) * (this.sign ? -1 : 1); 678 | }; 679 | BigInteger.prototype.compareTo = BigInteger.prototype.compare; 680 | 681 | SmallInteger.prototype.compare = function (v) { 682 | if (v === Infinity) { 683 | return -1; 684 | } 685 | if (v === -Infinity) { 686 | return 1; 687 | } 688 | 689 | var n = parseValue(v), 690 | a = this.value, 691 | b = n.value; 692 | if (n.isSmall) { 693 | return a == b ? 0 : a > b ? 1 : -1; 694 | } 695 | if (a < 0 !== n.sign) { 696 | return a < 0 ? -1 : 1; 697 | } 698 | return a < 0 ? 1 : -1; 699 | }; 700 | SmallInteger.prototype.compareTo = SmallInteger.prototype.compare; 701 | 702 | BigInteger.prototype.equals = function (v) { 703 | return this.compare(v) === 0; 704 | }; 705 | SmallInteger.prototype.eq = SmallInteger.prototype.equals = BigInteger.prototype.eq = BigInteger.prototype.equals; 706 | 707 | BigInteger.prototype.notEquals = function (v) { 708 | return this.compare(v) !== 0; 709 | }; 710 | SmallInteger.prototype.neq = SmallInteger.prototype.notEquals = BigInteger.prototype.neq = BigInteger.prototype.notEquals; 711 | 712 | BigInteger.prototype.greater = function (v) { 713 | return this.compare(v) > 0; 714 | }; 715 | SmallInteger.prototype.gt = SmallInteger.prototype.greater = BigInteger.prototype.gt = BigInteger.prototype.greater; 716 | 717 | BigInteger.prototype.lesser = function (v) { 718 | return this.compare(v) < 0; 719 | }; 720 | SmallInteger.prototype.lt = SmallInteger.prototype.lesser = BigInteger.prototype.lt = BigInteger.prototype.lesser; 721 | 722 | BigInteger.prototype.greaterOrEquals = function (v) { 723 | return this.compare(v) >= 0; 724 | }; 725 | SmallInteger.prototype.geq = SmallInteger.prototype.greaterOrEquals = BigInteger.prototype.geq = BigInteger.prototype.greaterOrEquals; 726 | 727 | BigInteger.prototype.lesserOrEquals = function (v) { 728 | return this.compare(v) <= 0; 729 | }; 730 | SmallInteger.prototype.leq = SmallInteger.prototype.lesserOrEquals = BigInteger.prototype.leq = BigInteger.prototype.lesserOrEquals; 731 | 732 | BigInteger.prototype.isEven = function () { 733 | return (this.value[0] & 1) === 0; 734 | }; 735 | SmallInteger.prototype.isEven = function () { 736 | return (this.value & 1) === 0; 737 | }; 738 | 739 | BigInteger.prototype.isOdd = function () { 740 | return (this.value[0] & 1) === 1; 741 | }; 742 | SmallInteger.prototype.isOdd = function () { 743 | return (this.value & 1) === 1; 744 | }; 745 | 746 | BigInteger.prototype.isPositive = function () { 747 | return !this.sign; 748 | }; 749 | SmallInteger.prototype.isPositive = function () { 750 | return this.value > 0; 751 | }; 752 | 753 | BigInteger.prototype.isNegative = function () { 754 | return this.sign; 755 | }; 756 | SmallInteger.prototype.isNegative = function () { 757 | return this.value < 0; 758 | }; 759 | 760 | BigInteger.prototype.isUnit = function () { 761 | return false; 762 | }; 763 | SmallInteger.prototype.isUnit = function () { 764 | return Math.abs(this.value) === 1; 765 | }; 766 | 767 | BigInteger.prototype.isZero = function () { 768 | return false; 769 | }; 770 | SmallInteger.prototype.isZero = function () { 771 | return this.value === 0; 772 | }; 773 | BigInteger.prototype.isDivisibleBy = function (v) { 774 | var n = parseValue(v); 775 | var value = n.value; 776 | if (value === 0) return false; 777 | if (value === 1) return true; 778 | if (value === 2) return this.isEven(); 779 | return this.mod(n).equals(Integer[0]); 780 | }; 781 | SmallInteger.prototype.isDivisibleBy = BigInteger.prototype.isDivisibleBy; 782 | 783 | function isBasicPrime(v) { 784 | var n = v.abs(); 785 | if (n.isUnit()) return false; 786 | if (n.equals(2) || n.equals(3) || n.equals(5)) return true; 787 | if (n.isEven() || n.isDivisibleBy(3) || n.isDivisibleBy(5)) return false; 788 | if (n.lesser(25)) return true; 789 | // we don't know if it's prime: let the other functions figure it out 790 | } 791 | 792 | BigInteger.prototype.isPrime = function () { 793 | var isPrime = isBasicPrime(this); 794 | if (isPrime !== undefined) return isPrime; 795 | var n = this.abs(), 796 | nPrev = n.prev(); 797 | var a = [2, 3, 5, 7, 11, 13, 17, 19], 798 | b = nPrev, 799 | d, t, i, x; 800 | while (b.isEven()) b = b.divide(2); 801 | for (i = 0; i < a.length; i++) { 802 | x = bigInt(a[i]).modPow(b, n); 803 | if (x.equals(Integer[1]) || x.equals(nPrev)) continue; 804 | for (t = true, d = b; t && d.lesser(nPrev); d = d.multiply(2)) { 805 | x = x.square().mod(n); 806 | if (x.equals(nPrev)) t = false; 807 | } 808 | if (t) return false; 809 | } 810 | return true; 811 | }; 812 | SmallInteger.prototype.isPrime = BigInteger.prototype.isPrime; 813 | 814 | BigInteger.prototype.isProbablePrime = function (iterations) { 815 | var isPrime = isBasicPrime(this); 816 | if (isPrime !== undefined) return isPrime; 817 | var n = this.abs(); 818 | var t = iterations === undefined ? 5 : iterations; 819 | // use the Fermat primality test 820 | for (var i = 0; i < t; i++) { 821 | var a = bigInt.randBetween(2, n.minus(2)); 822 | if (!a.modPow(n.prev(), n).isUnit()) return false; // definitely composite 823 | } 824 | return true; // large chance of being prime 825 | }; 826 | SmallInteger.prototype.isProbablePrime = BigInteger.prototype.isProbablePrime; 827 | 828 | BigInteger.prototype.modInv = function (n) { 829 | var t = bigInt.zero, newT = bigInt.one, r = parseValue(n), newR = this.abs(), q, lastT, lastR; 830 | while (!newR.equals(bigInt.zero)) { 831 | q = r.divide(newR); 832 | lastT = t; 833 | lastR = r; 834 | t = newT; 835 | r = newR; 836 | newT = lastT.subtract(q.multiply(newT)); 837 | newR = lastR.subtract(q.multiply(newR)); 838 | } 839 | if (!r.equals(1)) throw new Error(this.toString() + " and " + n.toString() + " are not co-prime"); 840 | if (t.compare(0) === -1) { 841 | t = t.add(n); 842 | } 843 | if (this.isNegative()) { 844 | return t.negate(); 845 | } 846 | return t; 847 | }; 848 | 849 | SmallInteger.prototype.modInv = BigInteger.prototype.modInv; 850 | 851 | BigInteger.prototype.next = function () { 852 | var value = this.value; 853 | if (this.sign) { 854 | return subtractSmall(value, 1, this.sign); 855 | } 856 | return new BigInteger(addSmall(value, 1), this.sign); 857 | }; 858 | SmallInteger.prototype.next = function () { 859 | var value = this.value; 860 | if (value + 1 < MAX_INT) return new SmallInteger(value + 1); 861 | return new BigInteger(MAX_INT_ARR, false); 862 | }; 863 | 864 | BigInteger.prototype.prev = function () { 865 | var value = this.value; 866 | if (this.sign) { 867 | return new BigInteger(addSmall(value, 1), true); 868 | } 869 | return subtractSmall(value, 1, this.sign); 870 | }; 871 | SmallInteger.prototype.prev = function () { 872 | var value = this.value; 873 | if (value - 1 > -MAX_INT) return new SmallInteger(value - 1); 874 | return new BigInteger(MAX_INT_ARR, true); 875 | }; 876 | 877 | var powersOfTwo = [1]; 878 | while (2 * powersOfTwo[powersOfTwo.length - 1] <= BASE) powersOfTwo.push(2 * powersOfTwo[powersOfTwo.length - 1]); 879 | var powers2Length = powersOfTwo.length, highestPower2 = powersOfTwo[powers2Length - 1]; 880 | 881 | function shift_isSmall(n) { 882 | return ((typeof n === "number" || typeof n === "string") && +Math.abs(n) <= BASE) || 883 | (n instanceof BigInteger && n.value.length <= 1); 884 | } 885 | 886 | BigInteger.prototype.shiftLeft = function (n) { 887 | if (!shift_isSmall(n)) { 888 | throw new Error(String(n) + " is too large for shifting."); 889 | } 890 | n = +n; 891 | if (n < 0) return this.shiftRight(-n); 892 | var result = this; 893 | if (result.isZero()) return result; 894 | while (n >= powers2Length) { 895 | result = result.multiply(highestPower2); 896 | n -= powers2Length - 1; 897 | } 898 | return result.multiply(powersOfTwo[n]); 899 | }; 900 | SmallInteger.prototype.shiftLeft = BigInteger.prototype.shiftLeft; 901 | 902 | BigInteger.prototype.shiftRight = function (n) { 903 | var remQuo; 904 | if (!shift_isSmall(n)) { 905 | throw new Error(String(n) + " is too large for shifting."); 906 | } 907 | n = +n; 908 | if (n < 0) return this.shiftLeft(-n); 909 | var result = this; 910 | while (n >= powers2Length) { 911 | if (result.isZero() || (result.isNegative() && result.isUnit())) return result; 912 | remQuo = divModAny(result, highestPower2); 913 | result = remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; 914 | n -= powers2Length - 1; 915 | } 916 | remQuo = divModAny(result, powersOfTwo[n]); 917 | return remQuo[1].isNegative() ? remQuo[0].prev() : remQuo[0]; 918 | }; 919 | SmallInteger.prototype.shiftRight = BigInteger.prototype.shiftRight; 920 | 921 | function bitwise(x, y, fn) { 922 | y = parseValue(y); 923 | var xSign = x.isNegative(), ySign = y.isNegative(); 924 | var xRem = xSign ? x.not() : x, 925 | yRem = ySign ? y.not() : y; 926 | var xDigit = 0, yDigit = 0; 927 | var xDivMod = null, yDivMod = null; 928 | var result = []; 929 | while (!xRem.isZero() || !yRem.isZero()) { 930 | xDivMod = divModAny(xRem, highestPower2); 931 | xDigit = xDivMod[1].toJSNumber(); 932 | if (xSign) { 933 | xDigit = highestPower2 - 1 - xDigit; // two's complement for negative numbers 934 | } 935 | 936 | yDivMod = divModAny(yRem, highestPower2); 937 | yDigit = yDivMod[1].toJSNumber(); 938 | if (ySign) { 939 | yDigit = highestPower2 - 1 - yDigit; // two's complement for negative numbers 940 | } 941 | 942 | xRem = xDivMod[0]; 943 | yRem = yDivMod[0]; 944 | result.push(fn(xDigit, yDigit)); 945 | } 946 | var sum = fn(xSign ? 1 : 0, ySign ? 1 : 0) !== 0 ? bigInt(-1) : bigInt(0); 947 | for (var i = result.length - 1; i >= 0; i -= 1) { 948 | sum = sum.multiply(highestPower2).add(bigInt(result[i])); 949 | } 950 | return sum; 951 | } 952 | 953 | BigInteger.prototype.not = function () { 954 | return this.negate().prev(); 955 | }; 956 | SmallInteger.prototype.not = BigInteger.prototype.not; 957 | 958 | BigInteger.prototype.and = function (n) { 959 | return bitwise(this, n, function (a, b) { return a & b; }); 960 | }; 961 | SmallInteger.prototype.and = BigInteger.prototype.and; 962 | 963 | BigInteger.prototype.or = function (n) { 964 | return bitwise(this, n, function (a, b) { return a | b; }); 965 | }; 966 | SmallInteger.prototype.or = BigInteger.prototype.or; 967 | 968 | BigInteger.prototype.xor = function (n) { 969 | return bitwise(this, n, function (a, b) { return a ^ b; }); 970 | }; 971 | SmallInteger.prototype.xor = BigInteger.prototype.xor; 972 | 973 | var LOBMASK_I = 1 << 30, LOBMASK_BI = (BASE & -BASE) * (BASE & -BASE) | LOBMASK_I; 974 | function roughLOB(n) { // get lowestOneBit (rough) 975 | // SmallInteger: return Min(lowestOneBit(n), 1 << 30) 976 | // BigInteger: return Min(lowestOneBit(n), 1 << 14) [BASE=1e7] 977 | var v = n.value, x = typeof v === "number" ? v | LOBMASK_I : v[0] + v[1] * BASE | LOBMASK_BI; 978 | return x & -x; 979 | } 980 | 981 | function integerLogarithm(value, base) { 982 | if (base.compareTo(value) <= 0) { 983 | var tmp = integerLogarithm(value, base.square(base)); 984 | var p = tmp.p; 985 | var e = tmp.e; 986 | var t = p.multiply(base); 987 | return t.compareTo(value) <= 0 ? { p: t, e: e * 2 + 1 } : { p: p, e: e * 2 }; 988 | } 989 | return { p: bigInt(1), e: 0 }; 990 | } 991 | 992 | BigInteger.prototype.bitLength = function () { 993 | var n = this; 994 | if (n.compareTo(bigInt(0)) < 0) { 995 | n = n.negate().subtract(bigInt(1)); 996 | } 997 | if (n.compareTo(bigInt(0)) === 0) { 998 | return bigInt(0); 999 | } 1000 | return bigInt(integerLogarithm(n, bigInt(2)).e).add(bigInt(1)); 1001 | } 1002 | SmallInteger.prototype.bitLength = BigInteger.prototype.bitLength; 1003 | 1004 | function max(a, b) { 1005 | a = parseValue(a); 1006 | b = parseValue(b); 1007 | return a.greater(b) ? a : b; 1008 | } 1009 | function min(a, b) { 1010 | a = parseValue(a); 1011 | b = parseValue(b); 1012 | return a.lesser(b) ? a : b; 1013 | } 1014 | function gcd(a, b) { 1015 | a = parseValue(a).abs(); 1016 | b = parseValue(b).abs(); 1017 | if (a.equals(b)) return a; 1018 | if (a.isZero()) return b; 1019 | if (b.isZero()) return a; 1020 | var c = Integer[1], d, t; 1021 | while (a.isEven() && b.isEven()) { 1022 | d = Math.min(roughLOB(a), roughLOB(b)); 1023 | a = a.divide(d); 1024 | b = b.divide(d); 1025 | c = c.multiply(d); 1026 | } 1027 | while (a.isEven()) { 1028 | a = a.divide(roughLOB(a)); 1029 | } 1030 | do { 1031 | while (b.isEven()) { 1032 | b = b.divide(roughLOB(b)); 1033 | } 1034 | if (a.greater(b)) { 1035 | t = b; b = a; a = t; 1036 | } 1037 | b = b.subtract(a); 1038 | } while (!b.isZero()); 1039 | return c.isUnit() ? a : a.multiply(c); 1040 | } 1041 | function lcm(a, b) { 1042 | a = parseValue(a).abs(); 1043 | b = parseValue(b).abs(); 1044 | return a.divide(gcd(a, b)).multiply(b); 1045 | } 1046 | function randBetween(a, b) { 1047 | a = parseValue(a); 1048 | b = parseValue(b); 1049 | var low = min(a, b), high = max(a, b); 1050 | var range = high.subtract(low).add(1); 1051 | if (range.isSmall) return low.add(Math.floor(Math.random() * range)); 1052 | var length = range.value.length - 1; 1053 | var result = [], restricted = true; 1054 | for (var i = length; i >= 0; i--) { 1055 | var top = restricted ? range.value[i] : BASE; 1056 | var digit = truncate(Math.random() * top); 1057 | result.unshift(digit); 1058 | if (digit < top) restricted = false; 1059 | } 1060 | result = arrayToSmall(result); 1061 | return low.add(typeof result === "number" ? new SmallInteger(result) : new BigInteger(result, false)); 1062 | } 1063 | var parseBase = function (text, base) { 1064 | var length = text.length; 1065 | var i; 1066 | var absBase = Math.abs(base); 1067 | for (var i = 0; i < length; i++) { 1068 | var c = text[i].toLowerCase(); 1069 | if (c === "-") continue; 1070 | if (/[a-z0-9]/.test(c)) { 1071 | if (/[0-9]/.test(c) && +c >= absBase) { 1072 | if (c === "1" && absBase === 1) continue; 1073 | throw new Error(c + " is not a valid digit in base " + base + "."); 1074 | } else if (c.charCodeAt(0) - 87 >= absBase) { 1075 | throw new Error(c + " is not a valid digit in base " + base + "."); 1076 | } 1077 | } 1078 | } 1079 | if (2 <= base && base <= 36) { 1080 | if (length <= LOG_MAX_INT / Math.log(base)) { 1081 | var result = parseInt(text, base); 1082 | if (isNaN(result)) { 1083 | throw new Error(c + " is not a valid digit in base " + base + "."); 1084 | } 1085 | return new SmallInteger(parseInt(text, base)); 1086 | } 1087 | } 1088 | base = parseValue(base); 1089 | var digits = []; 1090 | var isNegative = text[0] === "-"; 1091 | for (i = isNegative ? 1 : 0; i < text.length; i++) { 1092 | var c = text[i].toLowerCase(), 1093 | charCode = c.charCodeAt(0); 1094 | if (48 <= charCode && charCode <= 57) digits.push(parseValue(c)); 1095 | else if (97 <= charCode && charCode <= 122) digits.push(parseValue(c.charCodeAt(0) - 87)); 1096 | else if (c === "<") { 1097 | var start = i; 1098 | do { i++; } while (text[i] !== ">"); 1099 | digits.push(parseValue(text.slice(start + 1, i))); 1100 | } 1101 | else throw new Error(c + " is not a valid character"); 1102 | } 1103 | return parseBaseFromArray(digits, base, isNegative); 1104 | }; 1105 | 1106 | function parseBaseFromArray(digits, base, isNegative) { 1107 | var val = Integer[0], pow = Integer[1], i; 1108 | for (i = digits.length - 1; i >= 0; i--) { 1109 | val = val.add(digits[i].times(pow)); 1110 | pow = pow.times(base); 1111 | } 1112 | return isNegative ? val.negate() : val; 1113 | } 1114 | 1115 | function stringify(digit) { 1116 | if (digit <= 35) { 1117 | return "0123456789abcdefghijklmnopqrstuvwxyz".charAt(digit); 1118 | } 1119 | return "<" + digit + ">"; 1120 | } 1121 | 1122 | function toBase(n, base) { 1123 | base = bigInt(base); 1124 | if (base.isZero()) { 1125 | if (n.isZero()) return { value: [0], isNegative: false }; 1126 | throw new Error("Cannot convert nonzero numbers to base 0."); 1127 | } 1128 | if (base.equals(-1)) { 1129 | if (n.isZero()) return { value: [0], isNegative: false }; 1130 | if (n.isNegative()) 1131 | return { 1132 | value: [].concat.apply([], Array.apply(null, Array(-n)) 1133 | .map(Array.prototype.valueOf, [1, 0]) 1134 | ), 1135 | isNegative: false 1136 | }; 1137 | 1138 | var arr = Array.apply(null, Array(+n - 1)) 1139 | .map(Array.prototype.valueOf, [0, 1]); 1140 | arr.unshift([1]); 1141 | return { 1142 | value: [].concat.apply([], arr), 1143 | isNegative: false 1144 | }; 1145 | } 1146 | 1147 | var neg = false; 1148 | if (n.isNegative() && base.isPositive()) { 1149 | neg = true; 1150 | n = n.abs(); 1151 | } 1152 | if (base.equals(1)) { 1153 | if (n.isZero()) return { value: [0], isNegative: false }; 1154 | 1155 | return { 1156 | value: Array.apply(null, Array(+n)) 1157 | .map(Number.prototype.valueOf, 1), 1158 | isNegative: neg 1159 | }; 1160 | } 1161 | var out = []; 1162 | var left = n, divmod; 1163 | while (left.isNegative() || left.compareAbs(base) >= 0) { 1164 | divmod = left.divmod(base); 1165 | left = divmod.quotient; 1166 | var digit = divmod.remainder; 1167 | if (digit.isNegative()) { 1168 | digit = base.minus(digit).abs(); 1169 | left = left.next(); 1170 | } 1171 | out.push(digit.toJSNumber()); 1172 | } 1173 | out.push(left.toJSNumber()); 1174 | return { value: out.reverse(), isNegative: neg }; 1175 | } 1176 | 1177 | function toBaseString(n, base) { 1178 | var arr = toBase(n, base); 1179 | return (arr.isNegative ? "-" : "") + arr.value.map(stringify).join(''); 1180 | } 1181 | 1182 | BigInteger.prototype.toArray = function (radix) { 1183 | return toBase(this, radix); 1184 | }; 1185 | 1186 | SmallInteger.prototype.toArray = function (radix) { 1187 | return toBase(this, radix); 1188 | }; 1189 | 1190 | BigInteger.prototype.toString = function (radix) { 1191 | if (radix === undefined) radix = 10; 1192 | if (radix !== 10) return toBaseString(this, radix); 1193 | var v = this.value, l = v.length, str = String(v[--l]), zeros = "0000000", digit; 1194 | while (--l >= 0) { 1195 | digit = String(v[l]); 1196 | str += zeros.slice(digit.length) + digit; 1197 | } 1198 | var sign = this.sign ? "-" : ""; 1199 | return sign + str; 1200 | }; 1201 | 1202 | SmallInteger.prototype.toString = function (radix) { 1203 | if (radix === undefined) radix = 10; 1204 | if (radix != 10) return toBaseString(this, radix); 1205 | return String(this.value); 1206 | }; 1207 | BigInteger.prototype.toJSON = SmallInteger.prototype.toJSON = function () { return this.toString(); } 1208 | 1209 | BigInteger.prototype.valueOf = function () { 1210 | return parseInt(this.toString(), 10); 1211 | }; 1212 | BigInteger.prototype.toJSNumber = BigInteger.prototype.valueOf; 1213 | 1214 | SmallInteger.prototype.valueOf = function () { 1215 | return this.value; 1216 | }; 1217 | SmallInteger.prototype.toJSNumber = SmallInteger.prototype.valueOf; 1218 | 1219 | function parseStringValue(v) { 1220 | if (isPrecise(+v)) { 1221 | var x = +v; 1222 | if (x === truncate(x)) 1223 | return new SmallInteger(x); 1224 | throw new Error("Invalid integer: " + v); 1225 | } 1226 | var sign = v[0] === "-"; 1227 | if (sign) v = v.slice(1); 1228 | var split = v.split(/e/i); 1229 | if (split.length > 2) throw new Error("Invalid integer: " + split.join("e")); 1230 | if (split.length === 2) { 1231 | var exp = split[1]; 1232 | if (exp[0] === "+") exp = exp.slice(1); 1233 | exp = +exp; 1234 | if (exp !== truncate(exp) || !isPrecise(exp)) throw new Error("Invalid integer: " + exp + " is not a valid exponent."); 1235 | var text = split[0]; 1236 | var decimalPlace = text.indexOf("."); 1237 | if (decimalPlace >= 0) { 1238 | exp -= text.length - decimalPlace - 1; 1239 | text = text.slice(0, decimalPlace) + text.slice(decimalPlace + 1); 1240 | } 1241 | if (exp < 0) throw new Error("Cannot include negative exponent part for integers"); 1242 | text += (new Array(exp + 1)).join("0"); 1243 | v = text; 1244 | } 1245 | var isValid = /^([0-9][0-9]*)$/.test(v); 1246 | if (!isValid) throw new Error("Invalid integer: " + v); 1247 | var r = [], max = v.length, l = LOG_BASE, min = max - l; 1248 | while (max > 0) { 1249 | r.push(+v.slice(min, max)); 1250 | min -= l; 1251 | if (min < 0) min = 0; 1252 | max -= l; 1253 | } 1254 | trim(r); 1255 | return new BigInteger(r, sign); 1256 | } 1257 | 1258 | function parseNumberValue(v) { 1259 | if (isPrecise(v)) { 1260 | if (v !== truncate(v)) throw new Error(v + " is not an integer."); 1261 | return new SmallInteger(v); 1262 | } 1263 | return parseStringValue(v.toString()); 1264 | } 1265 | 1266 | function parseValue(v) { 1267 | if (typeof v === "number") { 1268 | return parseNumberValue(v); 1269 | } 1270 | if (typeof v === "string") { 1271 | return parseStringValue(v); 1272 | } 1273 | return v; 1274 | } 1275 | // Pre-define numbers in range [-999,999] 1276 | for (var i = 0; i < 1000; i++) { 1277 | Integer[i] = new SmallInteger(i); 1278 | if (i > 0) Integer[-i] = new SmallInteger(-i); 1279 | } 1280 | // Backwards compatibility 1281 | Integer.one = Integer[1]; 1282 | Integer.zero = Integer[0]; 1283 | Integer.minusOne = Integer[-1]; 1284 | Integer.max = max; 1285 | Integer.min = min; 1286 | Integer.gcd = gcd; 1287 | Integer.lcm = lcm; 1288 | Integer.isInstance = function (x) { return x instanceof BigInteger || x instanceof SmallInteger; }; 1289 | Integer.randBetween = randBetween; 1290 | 1291 | Integer.fromArray = function (digits, base, isNegative) { 1292 | return parseBaseFromArray(digits.map(parseValue), parseValue(base || 10), isNegative); 1293 | }; 1294 | 1295 | return Integer; 1296 | })(); 1297 | 1298 | // Node.js check 1299 | if (typeof module !== "undefined" && module.hasOwnProperty("exports")) { 1300 | module.exports = bigInt; 1301 | } 1302 | 1303 | //amd check 1304 | if (typeof define === "function" && define.amd) { 1305 | define("big-integer", [], function () { 1306 | return bigInt; 1307 | }); 1308 | } 1309 | -------------------------------------------------------------------------------- /js/config.js: -------------------------------------------------------------------------------- 1 | function mode(){ 2 | //return("test"); 3 | return("production"); 4 | }; 5 | -------------------------------------------------------------------------------- /js/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zack-bitcoin/amoveo-mining-pool/3810e11085a0e06cb54d8380f8ac9a8218f166cd/js/favicon.ico -------------------------------------------------------------------------------- /js/leaderboard.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | //todo. use {accounts} to download the accounts from the mining pool, display them neatly. 3 | //document.body.appendChild(document.createElement("br")); 4 | //document.body.appendChild(document.createElement("br")); 5 | 6 | var leaders = document.createElement("div"); 7 | document.body.appendChild(leaders); 8 | 9 | variable_public_get(["accounts"], make_leaderboard); 10 | function make_leaderboard(accs){ 11 | var a2 = understand_leaders(accs.slice(1)); 12 | console.log(JSON.stringify(a2)); 13 | return(display_leaders(a2)); 14 | }; 15 | function display_leaders(l) { 16 | if(l.length === 0) { 17 | return(0); 18 | } else { 19 | var pub = l[0][0]; 20 | var share_rate = l[0][1]; 21 | var l2 = l.slice(1); 22 | var p = document.createElement("p"); 23 | hashes_per_block( 24 | function(hpb){ 25 | var shares_per_hour = share_rate; 26 | var hashes_per_share = hpb.toJSNumber() / 1024; 27 | var hashes_per_hour = shares_per_hour * hashes_per_share; 28 | var hashes_per_second = hashes_per_hour / 3600; 29 | var gigahashes_per_second = hashes_per_second / 10000000000; 30 | p.innerHTML = "pub: " + pub + " gigahashes_per_second: " + Math.round(gigahashes_per_second).toString(); 31 | leaders.appendChild(p); 32 | return(display_leaders(l.slice(1))); 33 | }); 34 | }; 35 | }; 36 | function understand_leaders(l) { 37 | if(l.length === 0) { 38 | return([]); 39 | } else { 40 | var r = understand_leaders(l.slice(1)); 41 | var s = [l[0].slice(1)].concat(r); 42 | return(s); 43 | }; 44 | }; 45 | })(); 46 | -------------------------------------------------------------------------------- /js/lookup_account.js: -------------------------------------------------------------------------------- 1 | var pubkey = document.createElement("INPUT"); 2 | lookup_account1(); 3 | function lookup_account1() { 4 | document.body.appendChild(document.createElement("br")); 5 | document.body.appendChild(document.createElement("br")); 6 | var lookup_account = document.createElement("div"); 7 | document.body.appendChild(lookup_account); 8 | pubkey.setAttribute("type", "text"); 9 | var input_info = document.createElement("h8"); 10 | input_info.innerHTML = "pubkey: "; 11 | document.body.appendChild(input_info); 12 | document.body.appendChild(pubkey); 13 | 14 | var lookup_account_button = document.createElement("BUTTON"); 15 | var lookup_account_text_node = document.createTextNode("lookup account"); 16 | lookup_account_button.appendChild(lookup_account_text_node); 17 | lookup_account_button.onclick = lookup_account_helper; 18 | document.body.appendChild(lookup_account_button); 19 | function lookup_account_helper() { 20 | var x = pubkey.value; 21 | console.log("lookup account"); 22 | variable_public_get(["account", x], lookup_account_helper2); 23 | } 24 | function lookup_account_helper2(x) { 25 | console.log(x); 26 | var veo = x[2]; 27 | var shares = x[3]; 28 | var shares_per_hour = x[4]; 29 | hashes_per_block( 30 | function(hpb){ 31 | var hashes_per_share = hpb.toJSNumber() / 1024; 32 | var hashes_per_hour = shares_per_hour * hashes_per_share; 33 | var hashes_per_second = hashes_per_hour / 3600; 34 | var gigahashes_per_second = hashes_per_second / 10000000000; 35 | console.log(hpb); 36 | console.log(gigahashes_per_second); 37 | lookup_account.innerHTML = "veo: ".concat(veo / 100000000).concat(" shares: ").concat(shares).concat(" shares per hour: ").concat(shares_per_hour).concat(" gigahashes per second: ").concat(Math.round(gigahashes_per_second)); 38 | }); 39 | } 40 | 41 | 42 | } 43 | function exponent(a, b) {//a is type bigint. b is an int. 44 | if (b == 0) { return bigInt(1); } 45 | else if (b == 1) { return a; } 46 | else if ((b % 2) == 0) {return exponent(a.times(a), Math.floor(b / 2)); } 47 | else {return a.times(exponent(a, b-1)); } 48 | } 49 | function sci2int(x) { 50 | function pair2int(l) { 51 | var b = l.pop(); 52 | var a = l.pop(); 53 | var c = exponent(bigInt(2), a);//c is a bigint 54 | return c.times((256 + b)).divide(256); 55 | } 56 | function sci2pair(i) { 57 | var a = Math.floor(i / 256); 58 | var b = i % 256; 59 | return [a, b]; 60 | } 61 | return pair2int(sci2pair(x)); 62 | } 63 | 64 | var memoized_hashes_per_block = 0; 65 | function hashes_per_block(callback) { 66 | console.log("hashes per block0"); 67 | var port = 8080; 68 | if (mode() === "test"){ 69 | port = 3010; 70 | }; 71 | if (memoized_hashes_per_block === 0){ 72 | variable_public_get_port(["height"], port, function(height) { 73 | variable_public_get_port(["header", height], port, function(header){ 74 | console.log("hashes per block1"); 75 | var difficulty = header[6]; 76 | var hashes_per_block = sci2int(difficulty); 77 | memoized_hashes_per_block = hashes_per_block; 78 | return(callback(memoized_hashes_per_block)); 79 | }); 80 | }); 81 | } else { 82 | return(callback(memoized_hashes_per_block)); 83 | }; 84 | }; 85 | -------------------------------------------------------------------------------- /js/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

Amoveo Mining Pool

8 |

2048 shares = about 1 veo

9 |

Fee is currently set to 5%. you keep 95% of the mining rewards.

10 | click here to learn more about Amoveo.
11 | 12 | 13 | 14 | 15 | 16 | 17 |

you can publish a signed request to get an early payout.

18 | 19 |

leaderboard

20 | 21 | 22 | -------------------------------------------------------------------------------- /js/outstanding_shares.js: -------------------------------------------------------------------------------- 1 | (function outstanding_shares() { 2 | document.body.appendChild(document.createElement("br")); 3 | var div = document.createElement("div"); 4 | document.body.appendChild(div); 5 | 6 | var lookup = document.createElement("BUTTON"); 7 | var lookup_text_node = document.createTextNode("lookup outstanding shares"); 8 | lookup.appendChild(lookup_text_node); 9 | lookup.onclick = lookup_fun; 10 | var text2 = document.createElement("h8"); 11 | div.appendChild(lookup); 12 | div.appendChild(text2); 13 | 14 | function lookup_fun() { 15 | variable_public_get(["account", 2], function(total) { 16 | text2.innerHTML = "total shares: ".concat(total); 17 | }); 18 | } 19 | 20 | })(); 21 | -------------------------------------------------------------------------------- /js/payout.js: -------------------------------------------------------------------------------- 1 | (function payout() { 2 | document.body.appendChild(document.createElement("br")); 3 | var div = document.createElement("div"); 4 | document.body.appendChild(div); 5 | 6 | var button = document.createElement("input"); 7 | button.type = "button"; 8 | button.value = "generate unsigned request"; 9 | button.onclick = generate_unsigned_request; 10 | var unsigned_div = document.createElement("div"); 11 | var button2 = document.createElement("input"); 12 | button2.type = "button"; 13 | button2.value = "publish signed request"; 14 | button2.onclick = publish_signed_request; 15 | var signed = document.createElement("input"); 16 | signed.type = "text"; 17 | 18 | div.appendChild(button); 19 | div.appendChild(unsigned_div); 20 | div.appendChild(button2); 21 | div.appendChild(signed); 22 | 23 | var instructions = document.createElement("div"); 24 | instructions_link = document.createElement("a"); 25 | instructions_link.innerHTML = "light node"; 26 | instructions_link.href = "https://github.com/zack-bitcoin/light-node-amoveo"; 27 | instructions_link.target = "_blank"; 28 | instructions.innerHTML = "To sign the request use a "; 29 | instructions.appendChild(instructions_link); 30 | div.appendChild(instructions); 31 | //div.appendChild(instructions_link); 32 | 33 | function generate_unsigned_request(){ 34 | //request height from the full node 35 | variable_public_get(["height"], function(x) { 36 | var request = [-7, 27, pubkey.value, x]; 37 | unsigned_div.innerHTML = JSON.stringify(request); 38 | }); 39 | }; 40 | function publish_signed_request(){ 41 | var sr = JSON.parse(signed.value); 42 | variable_public_get(["spend", sr], function(x) { 43 | console.log("publish signed request"); 44 | }); 45 | }; 46 | })(); 47 | -------------------------------------------------------------------------------- /js/rpc.js: -------------------------------------------------------------------------------- 1 | function getter(t, u, callback){ 2 | var xmlhttp=new XMLHttpRequest(); 3 | xmlhttp.onreadystatechange = callback; 4 | xmlhttp.open("POST",u,true); 5 | xmlhttp.send(JSON.stringify(t)); 6 | return xmlhttp 7 | } 8 | function port_get(t, port, callback) { 9 | u = url(port, get_ip()); 10 | return getter(t, u, callback); 11 | } 12 | function get(t, callback) { 13 | u = url(get_port(), get_ip()); 14 | return getter(t, u, callback); 15 | } 16 | function url(port, ip) { return "http://".concat(ip).concat(":").concat(port.toString().concat("/")); } 17 | function xml_check(x) { 18 | return ((x.readyState === 4) && (x.status === 200)); }; 19 | function xml_out(x) { return x.responseText; } 20 | function refresh_helper(x, cmd, innercallback, callback, n) { 21 | if (n < 1) { return "failed to connect"; } 22 | else if (x.status == 400) { 23 | //the data we sent to the server got mixed up along the way, so it looks invalid to the server. 24 | //So lets re-send the command. 25 | setTimeout(function() { 26 | return variable_public_get(cmd, innercallback); 27 | }, 200); } 28 | else if (x.status == 0) { 29 | //this means that the server got our message, and it is still processing a response for us. So lets wait a bit, and then check if it is ready. 30 | setTimeout(function() { 31 | return refresh_helper(x, cmd, innercallback, 32 | callback, n - 1); 33 | }, 150); 34 | } 35 | else if (xml_check(x)) { 36 | //this means that the server got our message, and it sent a response. The response is ready to read, so lets read it. 37 | callback(xml_out(x));} 38 | else { 39 | //console.log(x.readyState); 40 | //console.log(x.status); 41 | setTimeout(function() {return refresh_helper(x, cmd, innercallback, callback, n);}, 10);} 42 | } 43 | 44 | my_status = "nil"; 45 | 46 | //function variable_get(cmd, callback) { 47 | // var x = local_get(cmd); 48 | // var_get(x, callback); 49 | //} 50 | function variable_public_get_port(cmd, port, callback) { 51 | var foobar = port_get(cmd, port); 52 | var_get(foobar, callback, cmd); 53 | } 54 | function variable_public_get(cmd, callback) { 55 | var foobar = get(cmd); 56 | var_get(foobar, callback, cmd); 57 | } 58 | function var_get(x, callback, cmd) { 59 | refresh_helper(x, cmd, callback, function(){ 60 | p = JSON.parse(xml_out(x)); 61 | callback(p[1]); 62 | }, 100); 63 | } 64 | 65 | -------------------------------------------------------------------------------- /js/server.js: -------------------------------------------------------------------------------- 1 | //make ip and port as input things. 2 | 3 | local_ip = [127,0,0,1]; 4 | local_port = 8081; 5 | var server_ip = document.createElement("INPUT"); 6 | server_ip.setAttribute("type", "text"); 7 | //server_ip.value = "159.89.106.253";// server 8 | server_ip.value = document.URL.split("/")[2].split(":")[0]; 9 | var server_ip_info = document.createElement("h8"); 10 | server_ip_info.innerHTML = "channel_node ip : "; 11 | var server_port = document.createElement("INPUT"); 12 | //server_port.value = "8080";// server 13 | server_port.value = document.URL.split(":")[2].substring(0, 4); 14 | server_port.setAttribute("type", "text"); 15 | var server_port_info = document.createElement("h8"); 16 | server_port_info.innerHTML = "port: "; 17 | //document.body.appendChild(server_ip_info); 18 | //document.body.appendChild(server_ip); 19 | //document.body.appendChild(server_port_info); 20 | //document.body.appendChild(server_port); 21 | 22 | document.body.appendChild(document.createElement("br")); 23 | var miner_location = document.createElement("h8"); 24 | miner_location.innerHTML = "point your miner here: http://".concat(server_ip.value).concat(":").concat(server_port.value); 25 | document.body.appendChild(miner_location); 26 | document.body.appendChild(document.createElement("br")); 27 | 28 | function get_port() { 29 | return parseInt(server_port.value, 10); 30 | } 31 | function get_ip() { 32 | //return JSON.parse(server_ip.value); 33 | return server_ip.value; 34 | } 35 | -------------------------------------------------------------------------------- /kill_all_erlang.sh: -------------------------------------------------------------------------------- 1 | for i in `ps -ef | grep erl | awk '{print $2}'`; do echo $i; kill -9 $i; done 2 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {deps, [ 3 | {pink_hash, "1", {git, "https://github.com/BumblebeeBat/pink_crypto", {branch, "master"}}}, 4 | {trie, "1", {git, "https://github.com/BumblebeeBat/MerkleTrie", {branch, "master"}}}, 5 | {cowboy, "2.10.0", {git, "https://github.com/ninenines/cowboy.git", {tag, "2.10.0"}}} 6 | ]}. 7 | 8 | {relx, [{release, { amoveo_mining_pool, "0.1.0" }, 9 | [amoveo_mining_pool, 10 | sasl]}, 11 | 12 | {sys_config, "./config/sys.config"}, 13 | {vm_args, "./config/vm.args"}, 14 | 15 | {dev_mode, true}, 16 | {include_erts, false}, 17 | 18 | {extended_start_script, true}] 19 | }. 20 | 21 | {profiles, [{prod, [{relx, [{dev_mode, true}, 22 | {include_erts, true}]}] 23 | }] 24 | }. 25 | 26 | {overrides, 27 | [{override, jiffy, [ 28 | {plugins, [pc]}, 29 | {artifacts, ["priv/jiffy.so"]}, 30 | {provider_hooks, [ 31 | {post, 32 | [ 33 | {compile, {pc, compile}}, 34 | {clean, {pc, clean}} 35 | ] 36 | }] 37 | } 38 | ]} 39 | ]}. -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | [{<<"cowboy">>, 2 | {git,"https://github.com/ninenines/cowboy.git", 3 | {ref,"9e600f6c1df3c440bc196b66ebbc005d70107217"}}, 4 | 0}, 5 | {<<"cowlib">>, 6 | {git,"https://github.com/ninenines/cowlib", 7 | {ref,"cc04201c1d0e1d5603cd1cde037ab729b192634c"}}, 8 | 1}, 9 | {<<"dump">>, 10 | {git,"https://github.com/BumblebeeBat/dump", 11 | {ref,"f76a31cb29ba2241f9d90978c7188dc6aeae31de"}}, 12 | 1}, 13 | {<<"encrypter">>, 14 | {git,"https://github.com/zack-bitcoin/encrypter", 15 | {ref,"304acda3f6148b041a44761c21e19be29dd9fcf5"}}, 16 | 1}, 17 | {<<"jiffy">>, 18 | {git,"https://github.com/davisp/jiffy", 19 | {ref,"9ea1b35b6e60ba21dfd4adbd18e7916a831fd7d4"}}, 20 | 2}, 21 | {<<"pink_hash">>, 22 | {git,"https://github.com/BumblebeeBat/pink_crypto", 23 | {ref,"fd79361eb3e7dc4e9ac5b36e38d58f3542755613"}}, 24 | 0}, 25 | {<<"ranch">>, 26 | {git,"https://github.com/ninenines/ranch", 27 | {ref,"a692f44567034dacf5efcaa24a24183788594eb7"}}, 28 | 1}, 29 | {<<"trie">>, 30 | {git,"https://github.com/BumblebeeBat/MerkleTrie", 31 | {ref,"2893945881349f6f409fe599564048c534b95c36"}}, 32 | 0}]. 33 | -------------------------------------------------------------------------------- /rebar3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zack-bitcoin/amoveo-mining-pool/3810e11085a0e06cb54d8380f8ac9a8218f166cd/rebar3 -------------------------------------------------------------------------------- /rebar3_old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zack-bitcoin/amoveo-mining-pool/3810e11085a0e06cb54d8380f8ac9a8218f166cd/rebar3_old -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | # First recompile the code and rebuild the release. 2 | ./rebar3 compile 3 | ./rebar3 as prod release 4 | # then launch the software 5 | ./_build/prod/rel/amoveo_mining_pool/bin/amoveo_mining_pool start 6 | -------------------------------------------------------------------------------- /todo: -------------------------------------------------------------------------------- 1 | we created reward_tracker.erl, which makes many other modules obsolete. 2 | 3 | accounts, rewards, rewards_pusher, and hashpower_leaders are probably all obsolete. 4 | 5 | we need a new way to track how much work is done by each miner. --------------------------------------------------------------------------------