├── clean.sh ├── rebar3 ├── config ├── sys.config.tmpl └── vm.args ├── js ├── favicon.ico ├── test.html ├── glossary.js ├── active_oracles.js ├── active_oracles.html ├── main.html ├── add.js ├── server.js ├── rpc.js ├── utils.js ├── main.js └── oracle_list.js ├── attach.sh ├── apps └── amoveo_p2p_derivatives_explorer │ └── src │ ├── pair_buy.erl │ ├── amoveo_p2p_derivatives_explorer.app.src │ ├── db.erl │ ├── amoveo_p2p_derivatives_explorer_app.erl │ ├── records.hrl │ ├── amoveo_p2p_derivatives_explorer_sup.erl │ ├── swap_markets.erl │ ├── swap_full.erl │ ├── file_handler.erl │ ├── utils.erl │ ├── talker.erl │ ├── swap_history.erl │ ├── binary_contracts.erl │ ├── buy_veo_orders.erl │ ├── scalar_contracts.erl │ ├── swap_verify.erl │ ├── http_handler.erl │ └── swap_books.erl ├── .gitignore ├── empty_gen_server.save ├── start.sh ├── rebar.config └── README.md /clean.sh: -------------------------------------------------------------------------------- 1 | rm _build/prod/rel/amoveo_p2p_derivatives_explorer/*.db 2 | -------------------------------------------------------------------------------- /rebar3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zack-bitcoin/Amoveo_p2p_derivatives_explorer/master/rebar3 -------------------------------------------------------------------------------- /config/sys.config.tmpl: -------------------------------------------------------------------------------- 1 | [ 2 | { amoveo_p2p_derivatives_explorer, [ 3 | {test_mode, false} 4 | ]} 5 | ]. 6 | -------------------------------------------------------------------------------- /js/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zack-bitcoin/Amoveo_p2p_derivatives_explorer/master/js/favicon.ico -------------------------------------------------------------------------------- /config/vm.args: -------------------------------------------------------------------------------- 1 | -sname amoveo_p2p_derivatives_explorer 2 | 3 | -setcookie amoveo_p2p_derivatives_explorer_cookie 4 | 5 | +K true 6 | +A30 7 | -------------------------------------------------------------------------------- /attach.sh: -------------------------------------------------------------------------------- 1 | ./_build/prod/rel/amoveo_p2p_derivatives_explorer/bin/amoveo_p2p_derivatives_explorer attach 2 | # ./_build/prod/rel/amoveo_p2p_derivatives_explorer/bin/amoveo_p2p_derivatives_explorer daemon_attach 3 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/pair_buy.erl: -------------------------------------------------------------------------------- 1 | -module(pair_buy). 2 | -export([]). 3 | 4 | -record(pair_buy_offer, 5 | {from, nonce, start_limit, 6 | end_limit, source_id, 7 | source_type, contract_hash, 8 | new_id, amount1, fee1, 9 | amount2, fee2, subs1, subs2}). 10 | 11 | 12 | -------------------------------------------------------------------------------- /js/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Amoveo P2P derivatives explorer - home

6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .edts 3 | *.iml 4 | _build 5 | /rel/ 6 | *.db 7 | *~ 8 | .#* 9 | \#* 10 | .rebar 11 | erl_crash.dump 12 | keys_backup 13 | lib/ 14 | bin/ 15 | .Python 16 | include/ 17 | *.pyc 18 | nosetests.xml 19 | pip-selfcheck.json 20 | config/*/sys.config 21 | config/sys.config 22 | man 23 | compile_commands.json 24 | deps/* 25 | rebar.lock 26 | .DS_Store -------------------------------------------------------------------------------- /empty_gen_server.save: -------------------------------------------------------------------------------- 1 | -module(). 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 | init(ok) -> {ok, []}. 5 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 6 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 7 | terminate(_, _) -> io:format("died!"), ok. 8 | handle_info(_, X) -> {noreply, X}. 9 | handle_cast(_, X) -> {noreply, X}. 10 | handle_call(_, _From, X) -> {reply, X, X}. -------------------------------------------------------------------------------- /js/glossary.js: -------------------------------------------------------------------------------- 1 | var glossary = (function(){ 2 | function helper(div, name) { 3 | return helper2(div, name, "(?)"); 4 | }; 5 | function helper2(div, name, s) { 6 | var x = document.createElement("a"); 7 | x.innerHTML = s; 8 | x.href = "https://github.com/zack-bitcoin/amoveo/tree/master/docs/light_node/glossary/".concat(name).concat(".md"); 9 | x.target = "_blank"; 10 | div.appendChild(x); 11 | }; 12 | return {link: helper, link2: helper2} 13 | })(); 14 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #set up a config file if it does not already exist. 2 | if [ ! -f config/sys.config ]; then 3 | cp config/sys.config.tmpl config/sys.config 4 | echo "File not found!" 5 | fi 6 | 7 | # First recompile the code and rebuild the release. 8 | ./rebar3 compile 9 | ./rebar3 as prod release 10 | # then launch the software 11 | ./_build/prod/rel/amoveo_p2p_derivatives_explorer/bin/amoveo_p2p_derivatives_explorer start 12 | # ./_build/prod/rel/amoveo_p2p_derivatives_explorer/bin/amoveo_p2p_derivatives_explorer daemon 13 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/amoveo_p2p_derivatives_explorer.app.src: -------------------------------------------------------------------------------- 1 | {application, amoveo_p2p_derivatives_explorer, 2 | [{description, "An OTP application"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {mod, { amoveo_p2p_derivatives_explorer_app, []}}, 6 | {applications, 7 | [kernel, 8 | stdlib, 9 | ssl, 10 | cowboy, 11 | inets, 12 | encrypter, 13 | jiffy, 14 | pink_hash, 15 | chalang, 16 | compiler 17 | ]}, 18 | {env,[]}, 19 | {modules, []}, 20 | 21 | {maintainers, []}, 22 | {licenses, ["Apache 2.0"]}, 23 | {links, []} 24 | ]}. 25 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/db.erl: -------------------------------------------------------------------------------- 1 | -module(db). 2 | -export([test/0, save/2, read/1]). 3 | save(F, X) -> file:write_file(F, term_to_binary(X)). 4 | read(F) -> 5 | case file:read_file(F) of 6 | {ok, <<>>} -> <<>>; 7 | {ok, Out} -> binary_to_term(Out); 8 | {error, enoent} -> 9 | %io:fwrite("file does not exist\n"), 10 | ""; 11 | {error, Reason} -> Reason 12 | end. 13 | -record(d, {a = "", b = "" }). 14 | test() -> 15 | X = #d{a=[1, 2, <<"abc">>, []], b = <<1,2,3,200>> }, 16 | File = "database.db", 17 | save(File, X), 18 | X = read(File), 19 | success. 20 | -------------------------------------------------------------------------------- /js/active_oracles.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var div = document.createElement("div"); 3 | document.body.appendChild(div); 4 | variable_public_get(["oracle_list", 2], function(X) { 5 | //console.log(JSON.stringify(X)); 6 | return display_oracles(X.slice(1)); 7 | }); 8 | function display_oracles(L) { 9 | if ((JSON.stringify(L)) == "[]") { 10 | return []; 11 | } 12 | var L1 = L[0]; 13 | var p = document.createElement("p"); 14 | p.innerHTML = ("").concat(L1[2][1]).concat(" ").concat(atob(L1[1])); 15 | div.appendChild(p); 16 | return display_oracles(L.slice(1)); 17 | }; 18 | })(); 19 | -------------------------------------------------------------------------------- /js/active_oracles.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Amoveo oracles explorer - home

6 | Here is a light node tool in your browser, you can use it to make derivatives for any of these oracles 7 |
8 | Return to the page for exploring available P2P derivative bets. 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /js/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

Amoveo P2P derivatives explorer - home

6 | Here is a light node tool in your browser, you can use it to participate in P2P derivatives contracts on Amoveo. 7 |
8 | Here you can see all the oracles available to bet on. 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/amoveo_p2p_derivatives_explorer_app.erl: -------------------------------------------------------------------------------- 1 | -module(amoveo_p2p_derivatives_explorer_app). 2 | -behaviour(application). 3 | -export([start/2, stop/1]). 4 | start(_StartType, _StartArgs) -> 5 | inets:start(), 6 | start_http(), 7 | swap_history:garbage_cron(), 8 | swap_books:garbage_cron(), 9 | spawn(fun() -> scalar_contracts:cron() end), 10 | amoveo_p2p_derivatives_explorer_sup:start_link(). 11 | stop(_State) -> 12 | ok. 13 | start_http() -> 14 | Dispatch = 15 | cowboy_router:compile( 16 | [{'_', [ 17 | {"/:file", file_handler, []}, 18 | {"/", http_handler, []} 19 | ]}]), 20 | %{ok, Port} = application:get_env(amoveo_mining_pool, port), 21 | Port = 8090, 22 | IP = {0,0,0,0}, 23 | {ok, _} = cowboy:start_clear( 24 | http, 25 | [{ip, IP}, {port, Port}], 26 | #{env => #{dispatch => Dispatch}}), 27 | ok. 28 | 29 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/records.hrl: -------------------------------------------------------------------------------- 1 | -record(swap_tx, {from, offer, fee}). 2 | -record(swap_tx2, {from, nonce, fee, offer, match_parts}). 3 | -record(swap_offer, { 4 | acc1, start_limit, end_limit, salt, 5 | amount1, cid1, type1, %this is what acc1 gives. 6 | amount2, cid2, type2, %this is what acc2 gives. 7 | fee1, %what acc1 pays in fees 8 | nonce}). 9 | -record(swap_offer2, { 10 | acc1, start_limit, end_limit, 11 | cid1, type1, amount1, 12 | cid2, type2, amount2, 13 | salt, start_nonce, parts}). 14 | -record(acc, {balance = 0, %amount of money you have 15 | nonce = 0, %increments with every tx you put on the chain. 16 | pubkey = <<>>, 17 | bets = 1,%This is a pointer to the merkel tree that stores how many bets you have made in each oracle. 18 | bets_hash = <<>>}). 19 | -record(sub_acc, {balance = 0, 20 | nonce = 0, 21 | pubkey, 22 | contract_id, 23 | type}). -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/amoveo_p2p_derivatives_explorer_sup.erl: -------------------------------------------------------------------------------- 1 | -module(amoveo_p2p_derivatives_explorer_sup). 2 | -behaviour(supervisor). 3 | -export([start_link/0, init/1, stop/0]). 4 | -define(SERVER, ?MODULE). 5 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 6 | -define(keys, [swap_history, 7 | swap_books, 8 | swap_full, 9 | swap_markets, 10 | scalar_contracts, 11 | binary_contracts, 12 | buy_veo_orders 13 | ]). 14 | %active_oracles, oracles, channel_offers_ram, channel_offers_hd, volume_order, close_offers]). 15 | start_link() -> 16 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 17 | child_killer([]) -> ok; 18 | child_killer([H|T]) -> 19 | supervisor:terminate_child(amoveo_p2p_derivatives_explorer_sup, H), 20 | child_killer(T). 21 | stop() -> child_killer(?keys). 22 | child_maker([]) -> []; 23 | child_maker([H|T]) -> [?CHILD(H, worker)|child_maker(T)]. 24 | init([]) -> 25 | Children = child_maker(?keys), 26 | {ok, { {one_for_all, 50000, 1}, Children} }. 27 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [debug_info]}. 2 | {deps, [ 3 | {cowboy, "2.10.0", {git, "https://github.com/ninenines/cowboy.git", {tag, "2.10.0"}}}, 4 | {pink_hash, "1", {git, "https://github.com/BumblebeeBat/pink_crypto", {tag, "master"}}}, 5 | {chalang, "1", {git, "https://github.com/zack-bitcoin/chalang", {tag, "master"}}}, 6 | {encrypter, "1", {git, "https://github.com/zack-bitcoin/encrypter", {tag, "master"}}} 7 | ]}. 8 | 9 | {relx, [{release, { amoveo_p2p_derivatives_explorer, "0.1.0" }, 10 | [amoveo_p2p_derivatives_explorer, 11 | sasl]}, 12 | 13 | {sys_config, "./config/sys.config"}, 14 | {vm_args, "./config/vm.args"}, 15 | 16 | {dev_mode, true}, 17 | {include_erts, false}, 18 | 19 | {extended_start_script, true}] 20 | }. 21 | 22 | {profiles, [{prod, [{relx, [{dev_mode, false}, 23 | {include_erts, true}]}] 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 | ]}. 40 | -------------------------------------------------------------------------------- /js/add.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var div = document.createElement("div"); 3 | document.body.appendChild(div); 4 | var status = document.createElement("p"); 5 | status.innerHTML = "add offer status: ready"; 6 | div.appendChild(status); 7 | div.appendChild(br()); 8 | var contract = text_input("channel offer: ", div); 9 | div.appendChild(br()); 10 | //var question_text = text_input("if the oracle does not yet exist, put the question text here: ", div); 11 | //div.appendChild(br()); 12 | //var expires = text_input("if the oracle does not yet exist, put the height when it becomes possible to trade here: ", div); 13 | //div.appendChild(br()); 14 | var Button = button_maker2("publish the channel offer", publish); 15 | div.appendChild(Button); 16 | function publish() { 17 | status.innerHTML = "add offer status: failed"; 18 | var c = JSON.parse(contract.value); 19 | //c = c.concat([[-7, 20 | // btoa(question_text.value), 21 | // parseInt(expires.value)]]); 22 | variable_public_get(["add", c], function(X) { 23 | status.innerHTML = "add offer status: successfully posted a trade"; 24 | }); 25 | }; 26 | })(); 27 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/swap_markets.erl: -------------------------------------------------------------------------------- 1 | %store a copy of data about the current set of markets here. 2 | 3 | %every few minutes we want to rescan the open trades, and make a new version of data to store here. 4 | 5 | -module(swap_markets). 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 | read/0, refresh/0, 9 | cron/0]). 10 | -record(x, {id, cid1, type1, cid2, type2}). 11 | init(ok) -> {ok, []}. 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(refresh, _) -> 17 | %scan all open trades 18 | X = swap_books:markets(), 19 | {noreply, X}; 20 | handle_cast(_, X) -> {noreply, X}. 21 | handle_call(read, _From, X) -> 22 | {reply, X, X}; 23 | handle_call(_, _From, X) -> {reply, X, X}. 24 | 25 | read() -> gen_server:call(?MODULE, read). 26 | refresh() -> gen_server:cast(?MODULE, refresh). 27 | 28 | cron() -> 29 | timer:sleep(5000), 30 | spawn(fun() -> 31 | refresh() 32 | end), 33 | spawn(fun() -> 34 | cron() 35 | end). 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | amoveo_p2p_derivatives_explorer 2 | ===== 3 | 4 | The purpose of this tool is so that people can make P2P derivatives offers from the amoveo light node, and post them to this website. The website organizes all the different trade offers that are posted. 5 | 6 | 7 | turn it on, then open this in a browser: http://localhost:8090/main.html 8 | 9 | 10 | Turning it on and off 11 | ======= 12 | 13 | First make sure you have an Amoveo node running and fully synced, and that the keys are unlocked on that node. 14 | 15 | If you need to resync your full node, be sure to turn off this p2p_derivatives tool first. 16 | 17 | ``` 18 | sh start.sh 19 | ``` 20 | 21 | To connect to it, so you can give it commands: 22 | ``` 23 | sh attach.sh 24 | ``` 25 | If it says "Node is not running!", this means that the Amoveo p2p derivatives explorer is shut off and there is nothing for you to connect to. So try using start.sh to turn it on. 26 | 27 | To disconnect, and allow it to run in the background, hold the CTRL key, and press D. 28 | 29 | Then to turn it off, make sure you are attached, and run: 30 | 31 | ``` 32 | utils:off(). 33 | ``` 34 | then run: 35 | ``` 36 | halt(). 37 | ``` 38 | 39 | Syncing smart contracts 40 | ============ 41 | ``` 42 | scalar_contracts:sync({159,89,87,58}, 8090). 43 | buy_veo_orders:sync({159,89,87,58}, 8090). 44 | ``` 45 | 46 | this will pull all the oracle text, and what you need to settle the scalar contracts. -------------------------------------------------------------------------------- /js/server.js: -------------------------------------------------------------------------------- 1 | //make ip and port as input things. 2 | 3 | local_ip = [127,0,0,1]; 4 | local_port = 8090; 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 | //console.log(server_port.value); 14 | var URL_REGEX = /^(https?)?(?:[\:\/]*)([a-z0-9\.-]*)(?:\:([0-9]+))?(\/[^?#]*)?(?:\?([^#]*))?(?:#(.*))?$/i; 15 | var match = document.URL.match(URL_REGEX); 16 | //console.log(match); 17 | if (match !== null){ 18 | var host = match[2]; 19 | var port = match[3]; 20 | console.log(port); 21 | if (document.URL[0] == 'f') { 22 | console.log("running stand-alone light node"); 23 | } else { 24 | console.log("running light node served from a full node"); 25 | server_port.value = port; 26 | // server_port.value = document.URL.split(":")[2].substring(0, 4); 27 | }; 28 | }; 29 | server_port.setAttribute("type", "text"); 30 | var server_port_info = document.createElement("h8"); 31 | server_port_info.innerHTML = "port: "; 32 | /* 33 | document.body.appendChild(server_ip_info); 34 | document.body.appendChild(server_ip); 35 | document.body.appendChild(server_port_info); 36 | document.body.appendChild(server_port); 37 | 38 | document.body.appendChild(document.createElement("br")); 39 | document.body.appendChild(document.createElement("br")); 40 | */ 41 | 42 | function get_port() { 43 | return parseInt(server_port.value, 10); 44 | } 45 | function get_ip() { 46 | //return JSON.parse(server_ip.value); 47 | return server_ip.value; 48 | } 49 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/swap_full.erl: -------------------------------------------------------------------------------- 1 | -module(swap_full). 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([read/1, add/3, remove/1]). 5 | 6 | %for storing the entire data of swap offers 7 | 8 | %-record(swap_offer, { 9 | % acc1, start_limit, end_limit, salt, 10 | % amount1, cid1, type1, %this is what acc1 gives. 11 | % amount2, cid2, type2, %this is what acc2 gives. 12 | % fee1, %what acc1 pays in fees 13 | % fee2}). 14 | %-record(swap_offer2, { 15 | % acc1, start_limit, end_limit, 16 | % cid1, type1, amount1, 17 | % cid2, type2, amount2, 18 | % salt, start_nonce, parts}). 19 | 20 | -define(LOC, "swap_full.db"). 21 | 22 | init(ok) -> 23 | process_flag(trap_exit, true), 24 | X = db:read(?LOC), 25 | Y = if 26 | (X == "") -> dict:new(); 27 | true -> X 28 | end, 29 | {ok, Y}. 30 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 31 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 32 | terminate(_, X) -> 33 | db:save(?LOC, X), 34 | io:format("swap full died!"), 35 | ok. 36 | handle_info(_, X) -> {noreply, X}. 37 | handle_cast({add, ID, S, T}, X) -> 38 | X2 = dict:store(ID, {S, T}, X), 39 | {noreply, X2}; 40 | handle_cast({remove, ID}, X) -> 41 | X2 = dict:erase(ID, X), 42 | {noreply, X2}; 43 | handle_cast(_, X) -> {noreply, X}. 44 | handle_call({read, ID}, _From, X) -> 45 | Z = dict:find(ID, X), 46 | {reply, Z, X}; 47 | handle_call(_, _From, X) -> {reply, X, X}. 48 | 49 | read(ID) -> gen_server:call(?MODULE, {read, ID}). 50 | add(ID, S, X) -> gen_server:cast(?MODULE, {add, ID, S, X}). 51 | remove(ID) -> gen_server:cast(?MODULE, {remove, ID}). 52 | 53 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/file_handler.erl: -------------------------------------------------------------------------------- 1 | -module(file_handler). 2 | 3 | -export([init/3, handle/2, terminate/3]). 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 | handle(Req, _) -> 8 | {F, _} = cowboy_req:path(Req), 9 | PrivDir0 = "../../../../js", 10 | PrivDir = list_to_binary(PrivDir0), 11 | true = case F of 12 | <<"/add.js">> -> true; 13 | <<"/active_oracles.js">> -> true; 14 | <<"/active_oracles.html">> -> true; 15 | <<"/glossary.js">> -> true; 16 | <<"/utils.js">> -> true; 17 | <<"/oracle_list.js">> -> true; 18 | <<"/favicon.ico">> -> true; 19 | <<"/main.html">> -> true; 20 | <<"/test.html">> -> true; 21 | <<"/main.js">> -> true; 22 | <<"/rpc.js">> -> true; 23 | <<"/server.js">> -> true; 24 | X -> 25 | io:fwrite("ext file handler block access to: "), 26 | io:fwrite(X), 27 | io:fwrite("\n"), 28 | false 29 | end, 30 | %File = << PrivDir/binary, <<"/external_web">>/binary, F/binary>>, 31 | File = << PrivDir/binary, F/binary>>, 32 | %{ok, _Data, _} = cowboy_req:body(Req), 33 | {ok, _Data, _} = cowboy_req:read_body(Req), 34 | Headers = [{<<"content-type">>, <<"text/html">>}, 35 | {<<"Access-Control-Allow-Origin">>, <<"*">>}], 36 | Text = read_file(File), 37 | {ok, Req2} = cowboy_req:reply(200, Headers, Text, Req), 38 | {ok, Req2, File}. 39 | read_file(F) -> 40 | {ok, File } = file:open(F, [read, binary, raw]), 41 | {ok, O} =file:pread(File, 0, filelib:file_size(F)), 42 | file:close(File), 43 | O. 44 | init(_Type, Req, _Opts) -> {ok, Req, []}. 45 | terminate(_Reason, _Req, _State) -> ok. 46 | -------------------------------------------------------------------------------- /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 get(t, callback) { 9 | return general_get(t, get_port(), callback) 10 | } 11 | function general_get(t, port, callback) { 12 | u = url(port, get_ip()); 13 | return getter(t, u, callback); 14 | } 15 | function url(port, ip) { return "http://".concat(ip).concat(":").concat(port.toString().concat("/")); } 16 | function xml_check(x) { 17 | return ((x.readyState === 4) && (x.status === 200)); }; 18 | function xml_out(x) { return x.responseText; } 19 | function refresh_helper(x, cmd, innercallback, callback, n) { 20 | if (n < 1) { return "failed to connect"; } 21 | else if (x.status == 400) { 22 | //the data we sent to the server got mixed up along the way, so it looks invalid to the server. 23 | //So lets re-send the command. 24 | setTimeout(function() { 25 | return variable_public_get(cmd, innercallback); 26 | }, 200); } 27 | else if (x.status == 0) { 28 | //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. 29 | setTimeout(function() { 30 | return refresh_helper(x, cmd, innercallback, 31 | callback, n - 1); 32 | }, 150); 33 | } 34 | else if (xml_check(x)) { 35 | //this means that the server got our message, and it sent a response. The response is ready to read, so lets read it. 36 | callback(xml_out(x));} 37 | else { 38 | //console.log(x.readyState); 39 | //console.log(x.status); 40 | setTimeout(function() {return refresh_helper(x, cmd, innercallback, callback, n);}, 10);} 41 | } 42 | 43 | my_status = "nil"; 44 | 45 | //function variable_get(cmd, callback) { 46 | // var x = local_get(cmd); 47 | // var_get(x, callback); 48 | //} 49 | function variable_public_get(cmd, callback) { 50 | var foobar = get(cmd); 51 | var_get(foobar, callback, cmd); 52 | } 53 | function var_get(x, callback, cmd) { 54 | refresh_helper(x, cmd, callback, function(){ 55 | p = JSON.parse(xml_out(x)); 56 | callback(p[1]); 57 | }, 100); 58 | } 59 | function messenger(cmd, callback) { 60 | var foobar = general_get(cmd, 8088); 61 | var_get(foobar, callback, cmd); 62 | } 63 | -------------------------------------------------------------------------------- /js/utils.js: -------------------------------------------------------------------------------- 1 | function br() { 2 | return document.createElement("br"); 3 | }; 4 | function button_maker2(val, fun) { 5 | var button = document.createElement("input"); 6 | button.type = "button"; 7 | button.value = val; 8 | button.onclick = fun; 9 | return button; 10 | }; 11 | function text(a) { 12 | var x2 = document.createElement("h8"); 13 | x2.innerHTML = a; 14 | return x2; 15 | }; 16 | function text_input(query, div) { 17 | var x = document.createElement("INPUT"); 18 | x.type = "text"; 19 | var q = text(query); 20 | div.appendChild(q); 21 | div.appendChild(x); 22 | return x; 23 | }; 24 | function token_units() { return 100000000 }; // VEO 25 | function s2c(x) { return x / token_units(); } 26 | function c2s(x) { 27 | return Math.floor(parseFloat(x.value, 10) * token_units()); 28 | } 29 | 30 | function oracle_limit(oid, callback) { 31 | return variable_public_get(["oracle", oid], function(x) { 32 | console.log(atob(x[1][4])); 33 | var question = atob(x[1][4]); 34 | //console.log(question); 35 | //measured_upper.value = (largest_number(question, 0, 0)).toString(); 36 | return callback(oracle_limit_grabber(question)); 37 | }); 38 | function oracle_limit_grabber(question) { 39 | //console.log("oracle limit grabber"); 40 | if (question.length < 4) { 41 | return ""; 42 | } 43 | var f = question.slice(0, 4); 44 | if (f == "from") { 45 | return olg2(question.slice(4)); 46 | } 47 | return oracle_limit_grabber(question.slice(1)); 48 | } 49 | function olg2(question) { 50 | //console.log("olg2"); 51 | //console.log(question); 52 | if (question.length < 2) { 53 | return ""; 54 | } 55 | var f = question.slice(0, 2); 56 | if (f == "to") { 57 | //console.log("calling olg3 "); 58 | return olg3(question.slice(2), ""); 59 | } 60 | return olg2(question.slice(1)); 61 | } 62 | function olg3(question, n) { 63 | //console.log(n); 64 | if (question.length < 1) { return n; } 65 | var l = question[0]; 66 | if (((l >= "0") && (l <= "9")) || (l == ".")) { 67 | var n2 = n.concat(l); 68 | return olg3(question.slice(1), n2); 69 | } else if (n == "") { 70 | return olg3(question.slice(1), n); 71 | } else { 72 | return n; 73 | } 74 | }; 75 | }; 76 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/utils.erl: -------------------------------------------------------------------------------- 1 | -module(utils). 2 | -export([cron_job/2, off/0, server_url/1, talk/1, 3 | trade_id/1, market_id/1]). 4 | 5 | -include("records.hrl"). 6 | 7 | test_mode() -> 8 | case application:get_env(amoveo_p2p_derivatives_explorer, test_mode) of 9 | {ok, B} -> B; 10 | _ -> false 11 | end. 12 | 13 | server_url(T) -> 14 | L = case T of 15 | internal -> "1"; 16 | external -> "0" 17 | end, 18 | TM = test_mode(), 19 | if 20 | TM -> "http://127.0.0.1:301"++L; 21 | true -> "http://127.0.0.1:808"++L 22 | end. 23 | talk(X) -> 24 | TM = test_mode(), 25 | Port = 26 | if 27 | TM -> 3011; 28 | true -> 8081 29 | end, 30 | talker:talk(X, {{127,0,0,1}, Port}). 31 | 32 | 33 | cron_job(Period, F) -> 34 | spawn(fun() -> 35 | timer:sleep(1000), 36 | cron2(F, Period) end). 37 | cron2(F, P) -> 38 | spawn(F), 39 | timer:sleep(P * 1000), 40 | cron2(F, P). 41 | 42 | off() -> 43 | amoveo_p2p_derivatives_explorer_sup:stop(), 44 | ok = application:stop(amoveo_p2p_derivatives_explorer). 45 | 46 | trade_id(Salt, Pub) -> 47 | hash:doit(<>). 49 | trade_id(SO) when is_record(SO, swap_offer2)-> 50 | #swap_offer2{ 51 | salt = Salt, 52 | acc1 = Acc 53 | } = SO, 54 | trade_id(Salt, Acc); 55 | trade_id(SO) when is_record(SO, swap_offer)-> 56 | #swap_offer{ 57 | salt = Salt, 58 | acc1 = Acc 59 | } = SO, 60 | trade_id(Salt, Acc). 61 | 62 | market_id(S) when is_record(S, swap_offer2) -> 63 | #swap_offer2{ 64 | cid1 = CID1, 65 | type1 = T1, 66 | cid2 = CID2, 67 | type2 = T2 68 | } = S, 69 | make_id(CID1, T1, CID2, T2); 70 | % hash:doit(<>); 74 | market_id(S) when is_record(S, swap_offer) -> 75 | #swap_offer{ 76 | cid1 = CID1, 77 | type1 = T1, 78 | cid2 = CID2, 79 | type2 = T2 80 | } = S, 81 | make_id(CID1, T1, CID2, T2). 82 | % hash:doit(<>). 86 | 87 | %make_id(CID1, Type1, CID2, Type2) -> 88 | % hash:doit(<>); 92 | make_id(CID1, Type1, CID2, Type2) -> 93 | <> = CID1, 94 | <> = CID2, 95 | if 96 | ((N1+Type1) =< (N2+Type2)) -> 97 | X = <>, 101 | %io:fwrite(base64:encode(X)), 102 | hash:doit(X); 103 | true -> 104 | make_id(CID2, Type2, CID1, Type1) 105 | end. 106 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | (function main() { 2 | console.log("hello world."); 3 | //This contract is valid as of block 64255 4 | //eventually it will become invalid, so if you want to run the test you need to make a different contract to replcae this one. 5 | /* 6 | If this test succeeds, the javascript console shows this: 7 | 8 | [-6,["v","6shH4FO3E3mZ7gBKwWv71NxT0FvUTqaVjhZ7ygMnfCI=",20000]] 9 | main.js:20 ["oracle","6shH4FO3E3mZ7gBKwWv71NxT0FvUTqaVjhZ7ygMnfCI=",[-6],[-6,"Xy9Tecb4Xx88W4D+NW2CQgYrDIM+9m3r7d/zy6YNe7o="],"UCA9IHRoZSBwcmljZSBvZiBVU0QgaW4gVkVPIG9uIHF0cmFkZS5pbyBmcm9tIDAgdG8gMC4wNSBvbiBNYXkgMzEsIDIwMTksIGF0IDEyOjAwIE5vb24gR01UOyByZXR1cm4gUCAqIDEwMjQgLyAwLjA1IGJpdCBudW1iZXIgMA==",69420] 10 | main.js:23 [-6,["channel_offer","Xy9Tecb4Xx88W4D+NW2CQgYrDIM+9m3r7d/zy6YNe7o=","6shH4FO3E3mZ7gBKwWv71NxT0FvUTqaVjhZ7ygMnfCI=",614,2,64339,10,10000,10000,2,"BBEuaxBNwXiTpEMTZI2gExMGpxCwAapTyFrgWMu5n4cIcqPojDz40Trf7xdWDlHL8KH+AvrTc2dhSC+35eSjmaQ="]] 11 | main.js:26 [-6,[-6,2,3000,5000,"BBEuaxBNwXiTpEMTZI2gExMGpxCwAapTyFrgWMu5n4cIcqPojDz40Trf7xdWDlHL8KH+AvrTc2dhSC+35eSjmaQ=",0,1000000,10000,10000,"6shH4FO3E3mZ7gBKwWv71NxT0FvUTqaVjhZ7ygMnfCI=",64239,1000,[-7,2,"MEQCIEvjwRnANgJrhLfKiPyd3YHSvFXL7XA098Acw9fXrS46AiByuwStQoVjBetI2+GNhmCHA569JSjSxqoAhhAoL+ZRgg=="],"AAD67xOHJw/qyEfgU7cTeZnuAErBa/vU3FPQW9ROppWOFnvKAyd8IjBFAiEA8Y2dZkonQU4QXfm6LZqK3les3GP3HlkXRXoJxbiIDY0CIEVn/yOB7CazFCHLeFGUjhk3XkTUVsWYQFkw4Pz2xCPy",1,2,"Xy9Tecb4Xx88W4D+NW2CQgYrDIM+9m3r7d/zy6YNe7o=",10,818,0,0],["signed",["nc_offer","BBEuaxBNwXiTpEMTZI2gExMGpxCwAapTyFrgWMu5n4cIcqPojDz40Trf7xdWDlHL8KH+AvrTc2dhSC+35eSjmaQ=",10,64339,10000,10000,1000,1000,"Xy9Tecb4Xx88W4D+NW2CQgYrDIM+9m3r7d/zy6YNe7o=","nPwN6mIWS4JUIo2neplltEEifucqc43ytORXToDFtco="],"MEUCIQCwxOaubh3Y7yuPBWZUKJy1jnhqYhLy+U1vRLZNO/pU1AIgX81qJ8HVp0r/Ac48tqG6F7yyYC7gKhcEka4qVk18G9U=",[-6]]] 12 | 13 | */ 14 | var contract = [-6,[-6,2,3000,5000,"BBEuaxBNwXiTpEMTZI2gExMGpxCwAapTyFrgWMu5n4cIcqPojDz40Trf7xdWDlHL8KH+AvrTc2dhSC+35eSjmaQ=",0,1000000,10000,10000,"6shH4FO3E3mZ7gBKwWv71NxT0FvUTqaVjhZ7ygMnfCI=",64239,1000,[-7,2,"MEQCIEvjwRnANgJrhLfKiPyd3YHSvFXL7XA098Acw9fXrS46AiByuwStQoVjBetI2+GNhmCHA569JSjSxqoAhhAoL+ZRgg=="],"AAD67xOHJw/qyEfgU7cTeZnuAErBa/vU3FPQW9ROppWOFnvKAyd8IjBFAiEA8Y2dZkonQU4QXfm6LZqK3les3GP3HlkXRXoJxbiIDY0CIEVn/yOB7CazFCHLeFGUjhk3XkTUVsWYQFkw4Pz2xCPy",1,2,"Xy9Tecb4Xx88W4D+NW2CQgYrDIM+9m3r7d/zy6YNe7o=",10,818,0,0],["signed",["nc_offer","BBEuaxBNwXiTpEMTZI2gExMGpxCwAapTyFrgWMu5n4cIcqPojDz40Trf7xdWDlHL8KH+AvrTc2dhSC+35eSjmaQ=",10,64339,10000,10000,1000,1000,"Xy9Tecb4Xx88W4D+NW2CQgYrDIM+9m3r7d/zy6YNe7o=","nPwN6mIWS4JUIo2neplltEEifucqc43ytORXToDFtco="],"MEUCIQCwxOaubh3Y7yuPBWZUKJy1jnhqYhLy+U1vRLZNO/pU1AIgX81qJ8HVp0r/Ac48tqG6F7yyYC7gKhcEka4qVk18G9U=",[-6]]]; 15 | variable_public_get(["add", contract], function(X) { 16 | variable_public_get(["oracle_list"], function(X) { 17 | console.log(JSON.stringify(X)); 18 | var OID = X[1][1]; 19 | variable_public_get(["oracle", OID], function(Oracle) { 20 | console.log(JSON.stringify(Oracle[1])); 21 | var CID = Oracle[1][3][1]; 22 | variable_public_get(["get_offers", [-6, CID]], function(Oracle) { 23 | console.log(JSON.stringify(Oracle)); 24 | }); 25 | variable_public_get(["get_offer_contract", CID], function(C) { 26 | console.log(JSON.stringify(C[1])); 27 | }); 28 | }); 29 | }); 30 | }); 31 | })(); 32 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/talker.erl: -------------------------------------------------------------------------------- 1 | -module(talker). 2 | -export([talk/2, talk/3, talk_timeout/3]). 3 | 4 | -define(RETRY, 3). 5 | 6 | talk_timeout(Msg, {IP, Port}, X) -> 7 | P = build_string_peer(IP, Port), 8 | talk_helper(Msg, P, ?RETRY, X). 9 | 10 | talk(Msg, {IP, Port}) -> 11 | talk(Msg, build_string_peer(IP, Port)); 12 | 13 | talk(Msg, Peer) -> 14 | talk_helper(Msg, Peer, ?RETRY, 20000). 15 | 16 | talk(Msg, IP, Port) -> 17 | talk(Msg, build_string_peer(IP, Port)). 18 | ip2string2(X) -> 19 | (integer_to_list(X)) ++ ("."). 20 | ip2string([A,B,C,D]) -> 21 | ip2string({A,B,C,D}); 22 | ip2string({A,B,C,D}) -> 23 | ip2string2(A) ++ 24 | ip2string2(B) ++ 25 | ip2string2(C) ++ 26 | integer_to_list(D). 27 | build_string_peer(IP, Port) -> 28 | T = ip2string(IP), 29 | P = integer_to_list(Port), 30 | "http://" ++ T ++ ":" ++ P ++ "/". 31 | check_print(S) -> 32 | io:fwrite(S). 33 | % case sync_mode:check() of 34 | % normal -> ok; 35 | % quick -> io:fwrite(S) 36 | % end. 37 | 38 | talk_helper(_, _, 0, _) -> 39 | check_print("talk helper fail\n"), 40 | bad_peer; 41 | %{error, failed_connect}; 42 | talk_helper(Msg, Peer, N, TimeOut) -> 43 | PM = packer:pack(Msg), 44 | %check_print("sending message "), 45 | %check_print(PM), 46 | %check_print("\n"), 47 | %timer:sleep(500), 48 | Msg = packer:unpack(PM), 49 | case httpc:request(post, {Peer, [], "application/octet-stream", iolist_to_binary(PM)}, [{timeout, TimeOut}], []) of 50 | {ok, {{_, 500, _}, _Headers, []}} -> 51 | check_print("server crashed. Will ignore peer. "), 52 | check_print(element(1, Msg)), 53 | check_print(" \n"), 54 | bad_peer; 55 | %talk_helper(Msg, Peer, 0, TimeOut); 56 | {ok, {Status, _Headers, []}} -> 57 | check_print("talk_helper weird response. Attempting to reconnect. \n"), 58 | check_print(packer:pack(Status)), 59 | talk_helper(Msg, Peer, N - 1, TimeOut); 60 | {ok, {_, _, R}} -> 61 | %check_print("talker peer is "), 62 | %check_print(Peer), 63 | %check_print("\n"), 64 | %check_print("talker msg is "), 65 | %check_print(packer:pack(Msg)), 66 | %check_print("\n"), 67 | %check_print("talker response is "), 68 | %check_print(R), 69 | %check_print("\n"), 70 | DoubleOK = packer:pack({ok, ok}), 71 | if 72 | R == DoubleOK -> 0; 73 | true -> 74 | packer:unpack(R) 75 | end; 76 | {error, socket_closed_remotely} -> 77 | %check_print("talk_helper socket closed remotely. attempting to reconnect \n"), 78 | talk_helper(Msg, Peer, N - 1, TimeOut); 79 | {error, timeout} -> 80 | check_print("talk_helper timeout. attempting to reconnect \n"), 81 | check_print(element(1, Msg)), 82 | check_print("\n"), 83 | talk_helper(Msg, Peer, N - 1, TimeOut); 84 | {error, failed_connect} -> 85 | check_print("talk_helper failed_connect 0. will ignore this peer. \n"), 86 | bad_peer; 87 | %talk_helper(Msg, Peer, N - 1, TimeOut); 88 | {error, {failed_connect, _}} -> 89 | %check_print("talk_helper failed_connect 1. will ignore this peer. \n"), 90 | %check_print(PM), 91 | bad_peer; 92 | %talk_helper(Msg, Peer, N - 1, TimeOut); 93 | X -> check_print("talk helper unexpected error"), 94 | check_print(X), 95 | error 96 | end. 97 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/swap_history.erl: -------------------------------------------------------------------------------- 1 | -module(swap_history). 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 | add/4, remove/2, read/2, garbage_cron/0 5 | ]). 6 | 7 | %this is for storing a history of how the swap order books were created. That way it is easy to follow along with the current state of the market by downloading the parts of the history that you missed. 8 | 9 | -include("records.hrl"). 10 | -define(LOC, "swap_history.db"). 11 | -record(sh, {nonce = 1, l}). 12 | -record(add, {tid, a1, a2, ts}). 13 | -record(remove, {tid, ts}). 14 | 15 | init(ok) -> 16 | process_flag(trap_exit, true), 17 | X = db:read(?LOC), 18 | Y = if 19 | (X == "") -> dict:new(); 20 | true -> X 21 | end, 22 | {ok, Y}. 23 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 24 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 25 | terminate(_, X) -> 26 | db:save(?LOC, X), 27 | io:format("swap history died!"), 28 | ok. 29 | handle_info(_, X) -> {noreply, X}. 30 | handle_cast(garbage, X) -> 31 | Keys = dict:fetch_keys(X), 32 | Now = erlang:timestamp(), 33 | X2 = garbage2(X, Now, Keys), 34 | {noreply, X2}; 35 | handle_cast({remove, MID, TID}, X) -> 36 | TS = erlang:timestamp(), 37 | case dict:find(MID, X) of 38 | empty -> 39 | io:fwrite("can't remove from a non-existant market"), 40 | {noreply, X}; 41 | {ok, #sh{nonce = N1, 42 | l = L}} -> 43 | SH2 = #sh{ 44 | nonce = N1 + 1, 45 | l = [#remove{tid = TID, ts = TS}|L] 46 | }, 47 | X2 = dict:store(MID, SH2, X), 48 | {noreply, X2} 49 | end; 50 | handle_cast(_, X) -> {noreply, X}. 51 | handle_call({add, MID, TID, A1, A2}, _, X) -> 52 | TS = erlang:timestamp(), 53 | E = #add{tid = TID, a1 = A1, a2 = A2, ts = TS}, 54 | %E = {add, TID, A1, A2, TS}, 55 | Y = case dict:find(MID, X) of 56 | error -> 57 | #sh{l = [E]}; 58 | {ok, #sh{nonce = N1, 59 | l = L}} -> 60 | #sh{ 61 | nonce = N1 + 1, 62 | l = [E|L] 63 | } 64 | end, 65 | X2 = dict:store(MID, Y, X), 66 | N = Y#sh.nonce, 67 | {reply, N, X2}; 68 | handle_call(read_all, _From, X) -> 69 | {reply, X, X}; 70 | handle_call({read, MarketID, Nonce}, _From, X) -> 71 | R = case dict:find(MarketID, X) of 72 | error -> []; 73 | A -> 74 | #sh{ 75 | nonce = N, 76 | l = L 77 | } = A, 78 | Many = min(length(L), N - Nonce), 79 | {L2, _} = lists:split(Many, L), 80 | L2 81 | end, 82 | {reply, R, X}; 83 | handle_call(_, _From, X) -> {reply, X, X}. 84 | 85 | 86 | add(MarketID, TradeID, Amount1, Amount2) -> 87 | gen_server:call(?MODULE, {add, MarketID, TradeID, Amount1, Amount2}). 88 | remove(MarketID, TradeID) -> 89 | gen_server:cast(?MODULE, {remove, MarketID, TradeID}). 90 | read(Market, Nonce) -> 91 | %returns history since Nonce. 92 | read2(Market, Nonce, 30000). 93 | read2(_, _, N) when N < 0 -> []; 94 | read2(Market, Nonce, N) -> 95 | case gen_server:call(?MODULE, {read, Market, Nonce}) of 96 | [] -> 97 | T = 100, 98 | timer:sleep(T), 99 | read2(Market, Nonce, N-T); 100 | X -> X 101 | end. 102 | garbage() -> 103 | gen_server:cast(?MODULE, garbage). 104 | 105 | garbage2(Dict, _, []) -> Dict; 106 | garbage2(Dict, Now, [M|T]) -> 107 | SH = dict:fetch(M, Dict), 108 | L = SH#sh.l, 109 | L2 = remove_old(L, Now), 110 | SH2 = SH#sh{l = L2}, 111 | Dict2 = dict:store(M, SH2, Dict), 112 | garbage2(Dict2, Now, T). 113 | remove_old([], _) -> []; 114 | remove_old([A|T], Now) when is_record(A, add) -> 115 | #add{ts = TS} = A, 116 | remove_old2(TS, A, T, Now); 117 | remove_old([R|T], Now) -> 118 | #remove{ts = TS} = R, 119 | remove_old2(TS, R, T, Now). 120 | 121 | remove_old2(TS, A, T, Now) -> 122 | Seconds = timer:now_diff(Now, TS) div 1000000, 123 | if 124 | Seconds > 600 -> %600 seconds is 10 minutes 125 | []; 126 | true -> [A|remove_old(T, Now)] 127 | end. 128 | 129 | 130 | garbage_cron() -> 131 | spawn(fun() -> 132 | timer:sleep(60000), 133 | garbage(), 134 | garbage_cron() 135 | end). 136 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/binary_contracts.erl: -------------------------------------------------------------------------------- 1 | -module(binary_contracts). 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 | add/2, add/4, read_contract/1]). 5 | 6 | %this is for storing the data needed to enforce the outcome for any binary contract type subcurrency being used 7 | %so if you want to post a swap tx, the subcurrencies being swapped need to have enforcement data. 8 | -define(LOC, "binary_contracts.db"). 9 | 10 | -record(binary, {text, height, now, source = <<0:256>>, source_type = 0}). 11 | 12 | init(ok) -> 13 | process_flag(trap_exit, true), 14 | X = db:read(?LOC), 15 | Y = if 16 | (X == "") -> dict:new(); 17 | true -> X 18 | end, 19 | {ok, Y}. 20 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 21 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 22 | terminate(_, X) -> 23 | db:save(?LOC, X), 24 | io:format("binary contracts died!"), 25 | ok. 26 | handle_info(_, X) -> {noreply, X}. 27 | handle_cast({add, CID, Text, Height, Source, SourceType}, X) -> 28 | Now = erlang:timestamp(), 29 | C = #binary{text = Text, 30 | height = Height, 31 | now = Now, 32 | source = Source, 33 | source_type = SourceType}, 34 | X2 = dict:store(CID, C, X), 35 | {noreply, X2}; 36 | handle_cast({add, CID, Text, Height}, X) -> 37 | Now = erlang:timestamp(), 38 | C = #binary{text = Text, height = Height, now = Now}, 39 | X2 = dict:store(CID, C, X), 40 | {noreply, X2}; 41 | handle_cast(_, X) -> {noreply, X}. 42 | handle_call({check, CID}, _From, X) -> 43 | {reply, dict:find(CID, X), X}; 44 | handle_call(_, _From, X) -> {reply, X, X}. 45 | 46 | add(OracleText, OracleHeight, Source, SourceType) -> 47 | CID = cid_maker(OracleText, OracleHeight, Source, SourceType), 48 | gen_server:cast(?MODULE, {add, CID, OracleText, OracleHeight, Source, SourceType}), 49 | CID. 50 | add(OracleText, OracleHeight) -> 51 | CID = cid_maker(OracleText, OracleHeight), 52 | gen_server:cast(?MODULE, {add, CID, OracleText, OracleHeight}), 53 | CID. 54 | 55 | read_contract(<<0:256>>) -> 56 | true; 57 | read_contract(CID) -> 58 | gen_server:call(?MODULE, {check, CID}). 59 | 60 | 61 | cid_maker(OracleText, OracleHeight) -> 62 | cid_maker(OracleText, OracleHeight, <<0:256>>, 0). 63 | cid_maker(OracleText, OracleHeight, Source, SourceType) -> 64 | true = is_binary(OracleText), 65 | QuestionHash = hash:doit(OracleText), 66 | OID = hash:doit(<>), 70 | BinaryCodeStatic = <<" OID ! \ 71 | macro [ nil ; \ 72 | macro , swap cons ; \ 73 | macro ] swap cons reverse ; \ 74 | macro even_split [ int 2147483648 , int 2147483647 , int 0 ] ; \ 75 | macro maximum int 4294967295 ; \ 76 | car drop car swap drop car swap drop car drop \ 77 | int 32 split \ 78 | OID @ \ 79 | == if else fail then \ 80 | drop drop int 1 split swap drop binary 3 AAAA swap ++ \ 81 | int 3 == if \ 82 | [ int 0 , int 0 , maximum ] int 0 int 1000 \ 83 | else drop \ 84 | int 1 == if \ 85 | [ maximum , int 0 , int 0 ] int 0 int 1000 \ 86 | else drop \ 87 | int 2 == if \ 88 | [ int 0 , maximum, int 0 ] int 0 int 1000 \ 89 | else drop drop \ 90 | even_split int 5000 int 10 \ 91 | then \ 92 | then \ 93 | then ">>, 94 | BinaryCodeInner = <<" binary 32 ", 95 | (base64:encode(OID))/binary, 96 | BinaryCodeStatic/binary 97 | >>, 98 | ContractBytecode = compiler_chalang:doit(BinaryCodeInner), 99 | BinaryHash = hash:doit(ContractBytecode), 100 | %BinaryCode = <<" def ", 101 | % BinaryCodeInner/binary, 102 | % " ; ">>, 103 | %BinaryDerivative = compiler_chalang:doit(BinaryCode), 104 | %BinaryHash = hd(vm(BinaryDerivative)), 105 | ToHash = <>, 109 | CID = hash:doit(ToHash), 110 | io:fwrite("binary contracts tohash is "), 111 | io:fwrite(packer:pack(ToHash)), 112 | io:fwrite("\n"), 113 | io:fwrite("cid is \n"), 114 | io:fwrite(packer:pack(CID)), 115 | io:fwrite("\n"), 116 | CID. 117 | %vm(Code) -> 118 | % ExampleData = chalang:data_maker(1000000,1000000,1000,1000,<<>>,<<>>,chalang:new_state(0,0,0),32,2,false), 119 | % chalang:stack(chalang:run5(Code, ExampleData)). 120 | 121 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/buy_veo_orders.erl: -------------------------------------------------------------------------------- 1 | -module(buy_veo_orders). 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 | add/1, read_contract/1, clean/0, 5 | keys/0, sync/2, cron/0]). 6 | 7 | -define(LOC, "buy_veo_orders.db"). 8 | -record(contract, 9 | {cid, source = <<0:256>>, 10 | source_type = 0, choose_address_timeout, 11 | oracle_start_height, blockchain, 12 | amount, ticker, date, trade_id, now 13 | }). 14 | 15 | init(ok) -> 16 | process_flag(trap_exit, true), 17 | X = db:read(?LOC), 18 | Y = if 19 | (X == "") -> dict:new(); 20 | true -> X 21 | end, 22 | {ok, Y}. 23 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 24 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 25 | terminate(_, X) -> 26 | db:save(?LOC, X), 27 | io:format("buy veo orders died!"), 28 | ok. 29 | handle_info(_, X) -> {noreply, X}. 30 | handle_cast({add, C1, CID2}, X) -> 31 | Now = erlang:timestamp(), 32 | C = C1#contract{ 33 | now = Now 34 | }, 35 | CID = C#contract.cid, 36 | %CID2 = cid_maker(C), 37 | %CID2 = CID, 38 | if 39 | (not(CID == CID2)) -> 40 | io:fwrite("buy veo orders, cids don't match\n"), 41 | {noreply, X}; 42 | true -> 43 | 44 | X2 = case dict:find(CID, X) of 45 | error -> 46 | io:fwrite("adding contract\n"), 47 | io:fwrite(packer:pack(CID)), 48 | io:fwrite("\n"), 49 | dict:store(CID, C, X); 50 | _ -> 51 | %io:fwrite(dict:find(CID, X)), 52 | %io:fwrite("\n"), 53 | io:fwrite("not adding contract, id taken\n"), 54 | X 55 | end, 56 | {noreply, X2} 57 | end; 58 | handle_cast(backup, X) -> 59 | db:save(?LOC, X), 60 | {noreply, X}; 61 | handle_cast({remove, CID}, X) -> 62 | %unused 63 | {noreply, dict:erase(CID, X)}; 64 | handle_cast(_, X) -> {noreply, X}. 65 | handle_call({read_contract, CID}, _From, X) -> 66 | {reply, dict:find(CID, X), X}; 67 | handle_call(keys, _From, X) -> 68 | {reply, dict:fetch_keys(X), X}; 69 | handle_call(_, _From, X) -> {reply, X, X}. 70 | 71 | cid_maker(Contract) -> 72 | #contract{ 73 | source = Source, 74 | source_type = SourceType, 75 | choose_address_timeout = 76 | ChooseAddressTimeout, 77 | oracle_start_height = OracleStartHeight, 78 | blockchain = Blockchain, 79 | amount = Amount, 80 | ticker = Ticker, 81 | date = Date, 82 | trade_id = TID 83 | } = Contract, 84 | PrivDir = "../../../../../amoveo/apps/amoveo_core/priv", 85 | {ok, CodeStatic} = file:read_file(PrivDir ++ "/buy_veo.fs"), 86 | {ok, CodeStatic2} = file:read_file(PrivDir ++ "/buy_veo_part2.fs"), 87 | 88 | OSHS = integer_to_binary(OracleStartHeight), 89 | ReusableSettings = 90 | <<" int4 ", 91 | OSHS/binary, 92 | " .\" ", 93 | Blockchain/binary, 94 | "\" .\" ", 95 | Amount/binary, 96 | "\" .\" ", 97 | Ticker/binary, 98 | "\" .\" ", 99 | Date/binary, 100 | "\" ">>, 101 | Settings = <<" int ", 102 | (integer_to_binary(ChooseAddressTimeout))/binary, 103 | " ", 104 | ReusableSettings/binary, 105 | " int 1 binary 32 ", 106 | (base64:encode(TID))/binary>>, 107 | %the 1 is the trade nonce. 108 | Gas = 100000, 109 | HashStatic2 = base64:encode(hd(chalang:stack(chalang:test(compiler_chalang:doit(<>), Gas, Gas, Gas, Gas, [])))), %contract hash of the static part. 110 | HashStatic2def = 111 | <<" macro part2 binary 32 ", 112 | HashStatic2/binary, 113 | " ; ">>, 114 | StaticBytes = compiler_chalang:doit(<>), 115 | SettingsBytes = compiler_chalang:doit(Settings), 116 | FullContract = <>, 117 | CH = hash:doit(FullContract), 118 | CID = hash:doit(<>), 122 | CID. 123 | 124 | 125 | 126 | clean() -> 127 | %TODO 128 | %if the offer expired, or was canceled. 129 | ok. 130 | sync(IP, Port) -> 131 | %TODO 132 | %add instructions to readme. 133 | 134 | %doit({contracts, 2}) -> 135 | {ok, CIDS} = 136 | talker:talk( 137 | {contracts, 2}, 138 | {IP, Port}), 139 | %sync_contracts([hd(CIDS)], {IP, Port}). 140 | sync_contracts(CIDS, {IP, Port}). 141 | sync_contracts([], _) -> ok; 142 | sync_contracts([CID|T], Peer) -> 143 | {ok, Contract} = 144 | talker:talk({read, 3, CID}, Peer), 145 | add(Contract), 146 | sync_contracts(T, Peer). 147 | 148 | backup() -> 149 | gen_server:cast(?MODULE, backup). 150 | keys() -> 151 | gen_server:call(?MODULE, keys). 152 | add(0) -> ok; 153 | add(Contract) when is_record(Contract, contract) -> 154 | %we can trust them to give the correct CID, because 256 bytes is too much space to find a collision, and because the light node can detect an incorrect cid from the contract data. 155 | %TODO, check the contract data is reasonably sized, and of the correct format. 156 | CID2 = cid_maker(Contract), 157 | gen_server:cast(?MODULE, {add, Contract, CID2}), 158 | CID2. 159 | 160 | read_contract(CID) -> 161 | gen_server:call(?MODULE, {read_contract, CID}). 162 | 163 | cron() -> 164 | timer:sleep(60000), 165 | backup(), 166 | spawn(fun() -> clean() end), 167 | cron(). 168 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/scalar_contracts.erl: -------------------------------------------------------------------------------- 1 | -module(scalar_contracts). 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 | add/3, add/5, read_contract/1, 5 | clean/0, keys/0, sync/2, 6 | cron/0]). 7 | 8 | %-record(c, {string, max_price, oracle_start_height}). 9 | 10 | %this is for storing the data needed to enforce the outcome for any scalar type contract 11 | %so if you want to post a swap tx, the subcurrencies being swapped need to have enforcement data. 12 | -define(LOC, "scalar_contracts.db"). 13 | -record(scalar, {text, height, max_price, now, source = <<0:256>>, source_type = 0}). 14 | 15 | init(ok) -> 16 | process_flag(trap_exit, true), 17 | X = db:read(?LOC), 18 | Y = if 19 | (X == "") -> dict:new(); 20 | true -> X 21 | end, 22 | {ok, Y}. 23 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 24 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 25 | terminate(_, X) -> 26 | db:save(?LOC, X), 27 | io:format("scalar contracts died!"), 28 | ok. 29 | handle_info(_, X) -> {noreply, X}. 30 | handle_cast({add, CID, Text, Height, MaxPrice, Source, SourceType}, X) -> 31 | Now = erlang:timestamp(), 32 | C = #scalar{text = Text, 33 | height = Height, 34 | max_price = MaxPrice, 35 | now = Now, 36 | source = Source, 37 | source_type = SourceType}, 38 | X2 = dict:store(CID, C, X), 39 | {noreply, X2}; 40 | handle_cast({add, CID, Text, Height, MaxPrice}, X) -> 41 | Now = erlang:timestamp(), 42 | C = #scalar{text = Text, 43 | height = Height, 44 | max_price = MaxPrice, 45 | now = Now}, 46 | X2 = dict:store(CID, C, X), 47 | {noreply, X2}; 48 | handle_cast({remove, CID}, X) -> 49 | {noreply, dict:erase(CID, X)}; 50 | handle_cast(backup, X) -> 51 | db:save(?LOC, X), 52 | {noreply, X}; 53 | handle_cast(_, X) -> {noreply, X}. 54 | handle_call(keys, _From, X) -> 55 | {reply, dict:fetch_keys(X), X}; 56 | handle_call({check, CID}, _From, X) -> 57 | {reply, dict:find(CID, X), X}; 58 | handle_call(_, _From, X) -> {reply, X, X}. 59 | 60 | sync(IP, Port) -> 61 | %talking to other server. 62 | {ok, CIDS} = 63 | talker:talk( 64 | {contracts}, 65 | {IP, Port}), 66 | sync_contracts(CIDS, {IP, Port}). 67 | sync_contracts([], _) -> ok; 68 | sync_contracts([CID|T], Peer) -> 69 | {ok, Contract} = 70 | talker:talk({read, 3, CID}, Peer), 71 | #scalar 72 | {text = Text, 73 | height = Height, 74 | max_price = MaxPrice, 75 | source = Source, 76 | source_type = SourceType 77 | } = Contract, 78 | add(Text, Height, MaxPrice, 79 | Source, SourceType), 80 | sync_contracts(T, Peer). 81 | 82 | clean() -> 83 | IN = utils:server_url(internal), 84 | {ok, H1} = talker:talk({height}, IN), 85 | {ok, H2} = talker:talk({height, 1}, IN), 86 | TestMode = case application:get_env(amoveo_p2p_derivatives_explorer, test_mode) of 87 | {ok, B} -> B; 88 | _ -> false 89 | end, 90 | if 91 | ((not TestMode) and (H1 < 130000)) -> 92 | io:fwrite("need to sync the full node first before cleaning scalar contracts"); 93 | not(H1 == H2) -> 94 | io:fwrite("need to sync the blocks in the full node first before syncing scalar contracts."); 95 | true -> 96 | D = gen_server:call(?MODULE, read_dict), 97 | clean_internal(D) 98 | end. 99 | clean_internal(D) -> 100 | Ks = dict:fetch_keys(D), 101 | clean_internal2(D, Ks). 102 | clean_internal2(_, []) -> ok; 103 | clean_internal2(D, [K|T]) -> 104 | A = dict:fetch(K, D), 105 | #scalar{ 106 | text = Question, 107 | now = Then, 108 | height = Start 109 | } = A, 110 | FN = utils:server_url(external), 111 | IN = utils:server_url(internal), 112 | {ok, Contract} = talker:talk({contracts, K}, FN), 113 | %and this contract was not created on-chain 114 | B1 = (Contract == 0), 115 | Now = erlang:timestamp(), 116 | DiffSeconds = 117 | timer:now_diff(Now, Then) / 1000000, 118 | %if more than 100 minutes passed, 119 | B2 = DiffSeconds > (60*100), 120 | %and we aren't storing any open offer for this. 121 | B3 = not(swap_books:using_contract(K)), 122 | QH = hash:doit(Question), 123 | OID = hash:doit(<>), 124 | {ok, Oracle} = talker:talk({oracle, OID}, IN), 125 | %if this oracle was already posted on-chain. 126 | B4 = not(Oracle == 0), 127 | if 128 | (B1 and B2 and B3) or 129 | (not(B1) and B4) -> 130 | gen_server:cast(?MODULE, {remove, K}); 131 | true -> ok 132 | end, 133 | clean_internal2(D, T). 134 | 135 | 136 | 137 | cid_maker(Text, Height, MaxPrice) -> 138 | cid_maker(Text, Height, MaxPrice, <<0:256>>, 0). 139 | cid_maker(Text, _Height, MaxPrice, Source, SourceType) -> 140 | true = is_binary(Text), 141 | StaticContract = base64:decode( 142 | "bpYZNRc5AzAyj4cUGIYWjDpGFBRHFHBxSG8AAAAAAXgAAAAAAngWAAAAAAN4gxSDFhSDFhSDFKyHAAAAAAJ5jBWGhgAAAAABeQAAAAADeYw6RhQUAgAAAAEwRxSQjIcWFBYCAAAAIGRuan/EdSKkhbAp0OEF6cQDv9x9li1vx5O6vqNMm3KlcUiGKIYoO0ZHDUiNhxYUAgAAAAEBO0ZHDUiEAAAAAAN5FoIA/////wAAAAADeTMWgoiMBAPo"), 143 | 144 | %"bpYZNRc5AzAyj4cUGIYWjDpGFBRHFHBxSG8AAAAAAXgAAAAAAngWAAAAAAN4gxSDFhSDFhSDFKyHAAAAAAF5jBWGhgAAAAACeQAAAAADeYw6RhQUAgAAAAEwRxSQjIcWFBYCAAAAIGRuan/EdSKkhbAp0OEF6cQDv9x9li1vx5O6vqNMm3KlcUiGKIYoO0ZHDUiNhxYUAgAAAAEBO0ZHDUiEAAAAAAN5FoIA/////wAAAAADeTMWgoiMBAPo"), 145 | OracleTextPart = "MaxPrice = " ++ integer_to_list(MaxPrice) ++ "; MaxVal = 4294967295; B = " ++ binary_to_list(Text) ++ " from $0 to $MaxPrice; max(0, min(MaxVal, (B * MaxVal / MaxPrice)) is ", 146 | L = length(OracleTextPart), 147 | %FullContract = <<0, Height:32, 2, L:32, (list_to_binary(OracleTextPart))/binary, StaticContract/binary>>, 148 | FullContract = <<22, 2, L:32, (list_to_binary(OracleTextPart))/binary, StaticContract/binary>>, 149 | CH = hash:doit(FullContract), 150 | CID = hash:doit(<>), 154 | CID. 155 | 156 | add(Text, Height, MaxPrice, Source, SourceType) -> 157 | CID = cid_maker(Text, Height, MaxPrice, Source, SourceType), 158 | gen_server:cast(?MODULE, {add, CID, Text, Height, MaxPrice, Source, SourceType}), 159 | CID. 160 | add(Text, Height, MaxPrice) -> 161 | CID = cid_maker(Text, Height, MaxPrice), 162 | gen_server:cast(?MODULE, {add, CID, Text, Height, MaxPrice}), 163 | CID. 164 | backup() -> 165 | gen_server:cast(?MODULE, backup). 166 | read_contract(<<0:256>>) -> 167 | true; 168 | read_contract(CID) -> 169 | gen_server:call(?MODULE, {check, CID}). 170 | keys() -> 171 | gen_server:call(?MODULE, keys). 172 | 173 | cron() -> 174 | timer:sleep(60000), 175 | backup(), 176 | spawn(fun() -> clean() end), 177 | cron(). 178 | -------------------------------------------------------------------------------- /js/oracle_list.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var div = document.createElement("div"); 3 | document.body.appendChild(div); 4 | 5 | var title2 = document.createElement("h3"); 6 | title2.innerHTML = "Raw Contract"; 7 | div.appendChild(title2); 8 | glossary.link2(div, "accepting_channel_offer", "how to accept a trade"); 9 | var contract_div = document.createElement("div"); 10 | div.appendChild(contract_div); 11 | 12 | var title1 = document.createElement("h3"); 13 | title1.innerHTML = "Trades"; 14 | div.appendChild(title1); 15 | var offers = document.createElement("div"); 16 | div.appendChild(offers); 17 | 18 | var title = document.createElement("h3"); 19 | title.innerHTML = "Oracles, click to see related trades"; 20 | div.appendChild(title); 21 | 22 | variable_public_get(["oracle_list"], function(X) { 23 | console.log(JSON.stringify(X)); 24 | var l = X.slice(1); 25 | offers.innerHTML = ""; 26 | display_oracles(l); 27 | }); 28 | function display_oracles(l) { 29 | if (JSON.stringify(l) == "[]") { 30 | return 0; 31 | } else { 32 | var h = l[0]; 33 | console.log(JSON.stringify(h)); 34 | variable_public_get(["oracle", h[1]], function(Oracle) { 35 | //variable_public_get(["oracle", h], function(Oracle) { 36 | if(Oracle == "error") { 37 | console.log("non existant oracle."); 38 | } else { 39 | console.log(JSON.stringify(Oracle)); 40 | var t = text(atob(Oracle[1][4])); 41 | var button = button_maker2("display trades", function() { return display_oracle(Oracle[1][2], Oracle[1][3]) }); 42 | div.appendChild(button); 43 | div.appendChild(t); 44 | div.appendChild(br()); 45 | }; 46 | //display_oracles(l.slice(1)); 47 | }); 48 | display_oracles(l.slice(1)); 49 | }; 50 | }; 51 | function display_oracle(Buys, Sells) { 52 | console.log(JSON.stringify([Buys, Sells])); 53 | var l = Buys.concat(Sells.slice(1)); 54 | variable_public_get(["get_offers", l], function(l2) { 55 | console.log(JSON.stringify(l2)); 56 | offers.innerHTML = ""; 57 | return display_offers(l2.slice(1)); 58 | }); 59 | }; 60 | function display_offers(l) { 61 | if (JSON.stringify(l) == "[]") { 62 | return 0; 63 | } else { 64 | var h = l[0]; 65 | var t = document.createElement("div"); 66 | var type; 67 | if (h[9] == 1) { 68 | type = "binary"; 69 | price = h[3]; 70 | return display_offers2(l, h, t, type, price, " or ", ""); 71 | } else if (h[9] == 2) { 72 | var oid = h[2]; 73 | type = "scalar" 74 | oracle_limit(oid, function(oracle_max) { 75 | console.log("oracle_list callback"); 76 | console.log(oracle_max); 77 | var direction = h[4]; 78 | if (direction == 2) { 79 | price = (1023 - h[3]) * oracle_max / 1023; 80 | } else if (direction == 1) { 81 | price = h[3] * oracle_max / 1023; 82 | } else { 83 | console.log("fail"); 84 | return 0 85 | }; 86 | return display_offers2(l, h, t, type, price, " veo/stablecoin or ", " stablecoin/veo;"); 87 | }); 88 | } else { 89 | console.log(h[9]); 90 | console.log("contract type not supported."); 91 | } 92 | } 93 | }; 94 | function display_offers2(l, h, t, type, price, d1message, d2message) { 95 | var direction; 96 | if (h[4] == 2) { 97 | if (type == "binary") { 98 | direction = "the result is true"; 99 | } else if (type == "scalar") { 100 | direction = "the price of stablecoin measured in veo increases"; 101 | } 102 | } else { 103 | if (type == "binary") { 104 | direction = "the result is false"; 105 | } else if (type == "scalar") { 106 | direction = "the price of stablecoin measured in veo decreases"; 107 | } 108 | } 109 | var text = "bet type: ".concat(type).concat("; price = ").concat(price.toFixed(5)).concat(d1message).concat((1/price).toFixed(5)).concat(d2message).concat(" you win if ").concat(direction).concat("; they pay = ").concat(s2c(h[7])).concat("; you pay = ").concat(s2c(h[8])).concat("; expires: ").concat(h[5]); 110 | t.innerHTML = text; 111 | offers.appendChild(t); 112 | var button = button_maker2("display a copy/pasteable version of this contract", function() { display_contract(h, type) }); 113 | offers.appendChild(button); 114 | offers.appendChild(br()); 115 | display_offers(l.slice(1)); 116 | }; 117 | function plus_encode(s) { 118 | if (s == "") { return ""; } 119 | var h = s[0]; 120 | if (h == "+") { h = "%2B"; } 121 | return h.concat(plus_encode(s.slice(1))); 122 | }; 123 | function display_contract(h, type) { 124 | var CID = h[1]; 125 | variable_public_get(["get_offer_contract", CID], function(C) { 126 | var copy_contract_link = document.createElement("div"); 127 | var contract_type = type; 128 | console.log(JSON.stringify(h)); 129 | console.log(JSON.stringify(C)); 130 | var oid = plus_encode(h[2]); 131 | var UL = C[1][1][18]; 132 | var LL = C[1][1][19]; 133 | F = function(X){ 134 | var Y = parseInt(X) / 100000000; 135 | return(Y.toFixed(8)); 136 | }; 137 | //var direction = C[1][2][1][2];//either 1 or 2. 138 | var direction = C[1][1][1];//either 1 or 2. 139 | var d_string; 140 | if (direction == 1) { 141 | d_string = "false"; 142 | } else if (direction == 2) { 143 | d_string = "true"; 144 | } else { 145 | console.log(JSON.stringify(C[1][2][1])); 146 | console.log(direction); 147 | console.log("badly formed contract offer"); 148 | return(0); 149 | } 150 | contract_div.innerHTML = JSON.stringify(C[1]); 151 | var our_amount = F(C[1][2][1][5]); 152 | var their_amount = F(C[1][2][1][4]); 153 | var oracle_height = C[1][1][22]; 154 | copy_contract_link.innerHTML = "open this contract in the contract-editor"); 155 | contract_div.appendChild(copy_contract_link); 156 | contract_div.appendChild(br()); 157 | //console.log(JSON.stringify(C[1])); 158 | }); 159 | }; 160 | })(); 161 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/swap_verify.erl: -------------------------------------------------------------------------------- 1 | -module(swap_verify). 2 | -export([doit/2, keep_longer/3, doit/3]). 3 | 4 | -include("records.hrl"). 5 | -record(trade, {nonce, value}). 6 | -define(verbose, true). 7 | 8 | doit(TID, S) -> 9 | FN = utils:server_url(external), 10 | {ok, Height} = talker:talk({height}, FN), 11 | doit(TID, S, Height). 12 | doit(TID, S, Height) -> 13 | %FN = utils:server_url(external), 14 | %%FNL = utils:server_url(internal), 15 | 16 | Offer = element(2, S), 17 | {SL, Fee1, Amount1, CID1, CID2, Acc1} = 18 | case Offer of 19 | #swap_offer{ 20 | start_limit = SL0, 21 | fee1 = Fee10, 22 | amount1 = Amount10, 23 | cid1 = CID10, 24 | cid2 = CID20, 25 | acc1 = Acc10 26 | } -> 27 | {SL0, Fee10, Amount10, 28 | CID10, CID20, Acc10}; 29 | #swap_offer2{ 30 | start_limit = SL1, 31 | amount1 = Amount11, 32 | cid1 = CID11, 33 | cid2 = CID21, 34 | acc1 = Acc11 35 | } -> 36 | {SL1, 0, Amount11, CID11, CID21, Acc11} 37 | end, 38 | 39 | B1 = sign:verify_sig(Offer, element(3, S), Acc1), 40 | 41 | %check acc1 is putting some minimum amount into it. 42 | %B2 = Fee1 + Amount1 > 1000000, 43 | B2 = true,%Fee1 + Amount1 > 1000000, 44 | 45 | %check that we aren't already storing a trade with this id 46 | B4 = error == swap_full:read(TID), 47 | 48 | %check that the start height limit has already occured. 49 | B5 = (Height >= SL), 50 | io:fwrite(packer:pack([Height, SL])), 51 | io:fwrite("\n"), 52 | 53 | %TODO verify that all the contract ids are in the binary_contracts database. `binary_contracts:read(CID)` 54 | B6 = ((not(error == binary_contracts:read_contract(CID1))) 55 | or (not(error == scalar_contracts:read_contract(CID1))) 56 | or (not(error == buy_veo_orders:read_contract(CID1)))), 57 | B7 = ((not(error == binary_contracts:read_contract(CID2))) 58 | or (not(error == scalar_contracts:read_contract(CID2))) 59 | or (not(error == buy_veo_orders:read_contract(CID2)))), 60 | B8 = B1 and B2 and B4 and B5 and B6 and B7, 61 | if 62 | (?verbose and (not B8))-> 63 | io:fwrite("swap verify, bad offer.\n"), 64 | io:fwrite(packer:pack([B1, B2, B4, B5, B6, B7])), 65 | io:fwrite("\n"), 66 | false; 67 | (not B8) -> false; 68 | true -> keep_longer(Offer, Height, TID) 69 | end. 70 | 71 | %keep_longer(Offer, Height, TID) when is_record(Offer, swap_offer) -> 72 | % 1=2, 73 | % %UNUSED VERSION 74 | % FNL = utils:server_url(internal), 75 | % FN = utils:server_url(external), 76 | % #swap_offer{ 77 | % end_limit = EL, 78 | % amount1 = Amount1, 79 | % %amount2 = Amount2, 80 | % salt = Salt, 81 | % acc1 = Acc1, 82 | % cid1 = CID1, 83 | % type1 = Type1, 84 | % nonce = Nonce 85 | % } = Offer, 86 | % 87 | % %check that it isn't expired. 88 | % B2 = (Height =< EL), 89 | % 90 | % %check the nonce. 91 | % {ok, [_, BlockHash]} = talker:talk({top, 1}, FNL), 92 | % B3 = case CID1 of 93 | % <<0:256>> -> 94 | % {ok, Acc} = talker:talk({account, Acc1, BlockHash}, FNL), 95 | % {ok, Acc0} = talker:talk({account, Acc1}, FNL), 96 | % if 97 | % Acc == 0 -> false; 98 | % true -> 99 | % (Acc0#acc.nonce =< Nonce) 100 | % and (Acc#acc.balance >= Amount1) 101 | % end; 102 | % _ -> 103 | % %Key = sub_accounts:make_key(Acc1, CID1, Type1), 104 | % {ok, SubAcc} = talker:talk({sub_account, Acc1, CID1, Type1, BlockHash}, FNL), 105 | % {ok, SubAcc0} = talker:talk({sub_account, Acc1, CID1, Type1}, FNL), 106 | % if 107 | % SubAcc == 0 -> false; 108 | % true -> 109 | % (SubAcc0#sub_acc.nonce =< Nonce) 110 | % and (SubAcc#sub_acc.balance >= Amount1) 111 | % end 112 | % end, 113 | % 114 | % %check that the trade id isn't already consumed 115 | % B4 = {ok, 0} == talker:talk({trade, TID}, FNL), 116 | % 117 | % {ok, Txs} = talker:talk({txs}, FN), 118 | % B5 = no_repeats(Acc1, Salt, Txs), 119 | % 120 | % B6 = B2 and B3 and B4 and B5, 121 | % if 122 | % not(B4) -> 123 | % trade_accepted; 124 | % (?verbose and not(B6)) -> 125 | % io:fwrite(packer:pack(swap_full:read(TID))), 126 | % io:fwrite("\n"), 127 | % io:fwrite(packer:pack([B2, B3, B4, B5])), 128 | % io:fwrite("\n"), 129 | % false; 130 | % not(B6) -> 131 | % false; 132 | % true -> 133 | % true 134 | % end; 135 | keep_longer(Offer, Height, TID) when is_record(Offer, swap_offer2)-> 136 | FNL = utils:server_url(internal), 137 | %FN = utils:server_url(external), 138 | #swap_offer2{ 139 | end_limit = EL, 140 | start_nonce = SN, 141 | parts = Parts, 142 | acc1 = Acc1, 143 | amount1 = Amount1, 144 | type1 = Type1, 145 | cid1 = CID1 146 | } = Offer, 147 | %check that it isn't expired. 148 | B2 = (Height < EL), 149 | %balance check 150 | %{ok, [_, BlockHash]} = talker:talk({top, 1}, FNL), 151 | B3 = case CID1 of 152 | <<0:256>> -> 153 | {ok, Acc} = talker:talk({account, Acc1}, FNL), 154 | if 155 | Acc == 0 -> false; 156 | true -> 157 | (Acc#acc.balance >= Amount1) 158 | end; 159 | _ -> true 160 | %{ok, SubAcc} = talker:talk({sub_account, Acc1, CID1, Type1}, FNL), 161 | %if 162 | % SubAcc == 0 -> false; 163 | % true -> 164 | % (SubAcc#sub_acc.balance >= Amount1) 165 | %end 166 | end, 167 | 168 | %check that the trade id isn't already consumed 169 | B4 = case talker:talk({trade, TID}, FNL) of 170 | {ok, 0} -> true; 171 | {ok, Trade} -> 172 | #trade{value = V} = Trade, 173 | V < (SN + Parts); 174 | X -> io:fwrite({X, TID}) 175 | end, 176 | %B4 = {ok, 0} == talker:talk({trade, TID}, FNL), 177 | B6 = B2 and B3 and B4, 178 | if 179 | not(B4) -> 180 | already_accepted; 181 | (?verbose and not(B6)) -> 182 | io:fwrite(packer:pack(swap_full:read(TID))), 183 | io:fwrite("\n"), 184 | io:fwrite(packer:pack([B2, B3, B4])), 185 | io:fwrite("\n"), 186 | false; 187 | not(B6) -> 188 | false; 189 | true -> 190 | true 191 | end. 192 | 193 | 194 | 195 | 196 | no_repeats(_, _, []) -> true; 197 | no_repeats(Acc, Salt, 198 | [{signed, 199 | #swap_tx{ 200 | offer = 201 | {signed, 202 | #swap_offer{ 203 | acc1 = Acc, 204 | salt = Salt 205 | }, 206 | _, _} 207 | }, _, _}|_]) -> 208 | false; 209 | no_repeats(Acc, Salt, [_|T]) -> 210 | no_repeats(Acc, Salt, T). 211 | 212 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/http_handler.erl: -------------------------------------------------------------------------------- 1 | -module(http_handler). 2 | -export([init/3, handle/2, terminate/3, doit/1, init/2]). 3 | -include("records.hrl"). 4 | init(Req0, Opts) -> 5 | handle(Req0, Opts). 6 | init(_Type, Req, _Opts) -> {ok, Req, no_state}. 7 | terminate(_Reason, _Req, _State) -> ok. 8 | handle(Req, State) -> 9 | %{ok, Data0, Req2} = cowboy_req:body(Req), 10 | {ok, Data0, Req2} = cowboy_req:read_body(Req), 11 | %{{IP, _}, Req3} = cowboy_req:peer(Req2), 12 | {IP, _} = cowboy_req:peer(Req2), 13 | %io:fwrite("http handler got message: "), 14 | %io:fwrite(Data0), 15 | %io:fwrite("\n"), 16 | Data1 = jiffy:decode(Data0), 17 | Data = packer:unpack_helper(Data1), 18 | D = packer:pack(doit(Data)), 19 | %Headers = #{ <<"content-type">> => <<"application/octet-stream">>, 20 | % <<"Access-Control-Allow-Origin">> => <<"*">>}, 21 | %Headers=[{<<"content-type">>,<<"application/octet-stream">>}, 22 | %{<<"Access-Control-Allow-Origin">>, <<"*">>}], 23 | Headers = #{ <<"content-type">> => <<"application/octet-stream">>, 24 | <<"Access-Control-Allow-Origin">> => <<"*">>}, 25 | %{ok, Req4} = cowboy_req:reply(200, Headers, D, Req2), 26 | Req4 = cowboy_req:reply(200, Headers, D, Req2), 27 | {ok, Req4, State}. 28 | doit({test}) -> {ok, "success"}; 29 | 30 | doit({add, 2, Text, Height}) -> 31 | CID = binary_contracts:add(Text, Height), 32 | {ok, CID}; 33 | doit({add, 3, Text, Height, MaxPrice, Source, SourceType}) -> 34 | io:fwrite("http handler.erl adding a scalar contract.\n"), 35 | CID = scalar_contracts:add(Text, Height, MaxPrice, Source, SourceType), 36 | {ok, CID}; 37 | doit({add, 3, Text, Height, MaxPrice}) -> 38 | CID = scalar_contracts:add(Text, Height, MaxPrice), 39 | {ok, CID}; 40 | doit({add, 4, BuyVeoContract}) -> 41 | io:fwrite("http handler adding buy veo contract \n"), 42 | CID = buy_veo_orders:add(BuyVeoContract), 43 | {ok, CID}; 44 | doit({add, SwapOffer, SecondOffer}) -> 45 | %gives the server a new swap offer. 46 | io:fwrite("http handler swap add start \n"), 47 | io:fwrite(packer:pack(SwapOffer)), 48 | io:fwrite("\n"), 49 | S = element(2, SwapOffer), 50 | TID = utils:trade_id(S), 51 | true = swap_verify:doit(TID, SwapOffer), 52 | true = 2000 > size(term_to_binary(SecondOffer)), 53 | {Amount1, Amount2, 54 | CID1, Type1, 55 | CID2, Type2} = 56 | case S of 57 | #swap_offer{} -> 58 | #swap_offer 59 | {amount1 = Amount10, 60 | amount2 = Amount20, 61 | cid1 = CID10, 62 | type1 = Type10, 63 | cid2 = CID20, 64 | type2 = Type20 65 | } = S, 66 | {Amount10, Amount20, 67 | CID10, Type10, 68 | CID20, Type20}; 69 | #swap_offer2{} -> 70 | #swap_offer2 71 | {amount1 = Amount11, 72 | amount2 = Amount21, 73 | cid1 = CID11, 74 | type1 = Type11, 75 | cid2 = CID21, 76 | type2 = Type21 77 | } = S, 78 | {Amount11, Amount21, 79 | CID11, Type11, 80 | CID21, Type21} 81 | end, 82 | <> = <<-1:32>>, 83 | Price = Amount1 * Max div Amount2, 84 | MID = utils:market_id(S), 85 | Nonce = swap_history:add(MID, TID, Amount1, Amount2),%cronological order, so we can sync faster. 86 | swap_books:add(MID, TID, Price, Amount1, Nonce, CID1, Type1, CID2, Type2),%order book 87 | swap_full:add(TID, SwapOffer, SecondOffer),%full swap data 88 | io:fwrite("http handler swap add end \n"), 89 | {ok, 0}; 90 | doit({add, 2, Text, Height, Source, SourceType}) -> 91 | CID = binary_contracts:add(Text, Height, Source, SourceType), 92 | {ok, CID}; 93 | doit({contracts}) -> 94 | {ok, scalar_contracts:keys()}; 95 | doit({contracts, 2}) -> 96 | {ok, buy_veo_orders:keys()}; 97 | doit({read, 3, CID}) -> 98 | case binary_contracts:read_contract(CID) of 99 | error -> 100 | case scalar_contracts:read_contract(CID) of 101 | error -> 102 | case buy_veo_orders:read_contract(CID) of 103 | error -> {ok, 0}; 104 | Z -> Z 105 | end; 106 | Y -> Y 107 | end; 108 | X -> X 109 | end; 110 | doit({history, MID, Nonce}) -> 111 | %returns the history of updates to market ID since Nonce. 112 | %if Nonce is up to date, it waits a while before responding. 113 | {ok, swap_history:read(MID, Nonce)}; 114 | doit({read, 2, ID}) -> 115 | %returns the full content of the trade with this ID 116 | {ok, {X, _}} = swap_full:read(ID), 117 | {ok, X}; 118 | doit({read, ID}) -> 119 | %returns the current state of market ID 120 | {ok, swap_books:read(ID)}; 121 | doit({markets}) -> 122 | %list of active markets. 123 | %we should also say something about the currencies being traded in each market. 124 | %X = swap_markets:read(), 125 | X = swap_books:markets(), 126 | {ok, X}; 127 | doit({markets, 2}) -> 128 | X = swap_books:markets2(), 129 | {ok, X}; 130 | 131 | %doit({oracle_list, 2}) -> 132 | % {ok, active_oracles:read()}; 133 | %doit({oracle_list}) -> 134 | % {ok, volume_order:read()}; 135 | %doit({oracle, OID}) -> 136 | % {ok, oracles:read(OID)}; 137 | %doit({get_offers, L}) ->%list of CIDs 138 | % FN = utils:server_url(external), 139 | % L2 = case talker:talk({txs}, FN) of 140 | % bad_peer -> L; 141 | % {ok, Txs} -> 142 | % BadCIDs = read_filter(Txs), 143 | % list_subtract(L, BadCIDs) 144 | % end, 145 | % {ok, channel_offers_ram:read(L2)}; 146 | %doit({get_offers, 2, L}) ->%list of Oracle IDs 147 | % S1 = lists:map(fun(X) -> oracles:read(X) end, 148 | % L), 149 | % R = get_offers_loop(S1, L, erlang:timestamp(), 60), 150 | %grab starting state of L. 151 | %keep checking to see if it changes. 152 | %if R is 0, nothing changed. if it is 1, then something changed. 153 | % {ok, R}; 154 | %doit({get_offer_contract, CID}) -> 155 | % {ok, channel_offers_hd:read(CID)}; 156 | %doit({add, 2, X}) -> {ok, close_offers:add(X)}; 157 | %doit({read, 2, X}) -> {ok, close_offers:read(X)}; 158 | doit(X) -> 159 | io:fwrite("http handler doit fail"), 160 | io:fwrite(X), 161 | error. 162 | 163 | is_in(X, []) -> false; 164 | is_in(X, [X|_]) -> true; 165 | is_in(X, [_|T]) -> 166 | is_in(X, T). 167 | 168 | %test() -> 169 | % C = <<"[-6,[-6,2,3000,5000,\"BBEuaxBNwXiTpEMTZI2gExMGpxCwAapTyFrgWMu5n4cIcqPojDz40Trf7xdWDlHL8KH+AvrTc2dhSC+35eSjmaQ=\",0,1000000,10000,10000,\"6shH4FO3E3mZ7gBKwWv71NxT0FvUTqaVjhZ7ygMnfCI=\",64239,1000,[-7,2,\"MEQCIEvjwRnANgJrhLfKiPyd3YHSvFXL7XA098Acw9fXrS46AiByuwStQoVjBetI2+GNhmCHA569JSjSxqoAhhAoL+ZRgg==\"],\"AAD67xOHJw/qyEfgU7cTeZnuAErBa/vU3FPQW9ROppWOFnvKAyd8IjBFAiEA8Y2dZkonQU4QXfm6LZqK3les3GP3HlkXRXoJxbiIDY0CIEVn/yOB7CazFCHLeFGUjhk3XkTUVsWYQFkw4Pz2xCPy\",1,2,\"Xy9Tecb4Xx88W4D+NW2CQgYrDIM+9m3r7d/zy6YNe7o=\",10,818,0,0],[\"signed\",[\"nc_offer\",\"BBEuaxBNwXiTpEMTZI2gExMGpxCwAapTyFrgWMu5n4cIcqPojDz40Trf7xdWDlHL8KH+AvrTc2dhSC+35eSjmaQ=\",10,64339,10000,10000,1000,1000,\"Xy9Tecb4Xx88W4D+NW2CQgYrDIM+9m3r7d/zy6YNe7o=\",\"nPwN6mIWS4JUIo2neplltEEifucqc43ytORXToDFtco=\"],\"MEUCIQCwxOaubh3Y7yuPBWZUKJy1jnhqYhLy+U1vRLZNO/pU1AIgX81qJ8HVp0r/Ac48tqG6F7yyYC7gKhcEka4qVk18G9U=\",[-6]]]">>, 170 | % doit({add, packer:unpack(C)}), 171 | % http_handler:doit({oracle_list}). 172 | 173 | %read_filter([]) -> []; 174 | %read_filter([{signed, T, _, _}|R]) when (element(1, T) == nc_accept) -> 175 | % SOffer = element(3, T), 176 | % {signed, Offer, _, _} = SOffer, 177 | % CID = element(9, Offer), 178 | % [CID|read_filter(R)]; 179 | %read_filter([_|T]) -> 180 | % read_filter(T). 181 | %list_subtract([], _) -> []; 182 | %list_subtract([H|T], L) -> 183 | % B = is_in(H, L), 184 | % if 185 | % B -> list_subtract(T, L); 186 | % true -> [H|list_subtract(T, L)] 187 | % end. 188 | 189 | 190 | %get_offers_loop(S1, L, TS, TimeLimit) -> 191 | % Now = erlang:timestamp(), 192 | % D = timer:now_diff(Now, TS) div 1000000,%in seconds 193 | % S2 = lists:map(fun(X) -> oracles:read(X) end, 194 | % L),%we could use a different database that keeps a record of thetimes when each oracle is changed. That way we don't need to read so much data out of a slow dictionary. 195 | % if 196 | % D > TimeLimit -> 0; 197 | % S1 == S2 -> 198 | % timer:sleep(100),%miliseconds 199 | % get_offers_loop(S1, L, TS, TimeLimit); 200 | % true -> 1 201 | % end. 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /apps/amoveo_p2p_derivatives_explorer/src/swap_books.erl: -------------------------------------------------------------------------------- 1 | -module(swap_books). 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 | add/9, read/1, markets/0, markets2/0, garbage_cron/0, using_contract/1, 5 | garbage/0 6 | ]). 7 | 8 | %this is for storing the current order books for all the markets of swap offers. 9 | 10 | 11 | -define(LOC, "swap_books.db"). 12 | -define(cron, 60000).%for production we probably want this higher than 1 second. 13 | -include("records.hrl"). 14 | 15 | -record(market, {nonce = 1, mid, cid1, type1, cid2, type2, orders}). 16 | -record(order, {price, amount, tid}). 17 | 18 | new_order(TID, Price, Amount) -> 19 | #order{tid = TID, 20 | price = Price, 21 | amount = Amount}. 22 | 23 | init(ok) -> 24 | process_flag(trap_exit, true), 25 | X = db:read(?LOC), 26 | Y = if 27 | (X == "") -> dict:new(); 28 | true -> X 29 | end, 30 | {ok, Y}. 31 | 32 | start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, ok, []). 33 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 34 | terminate(_, X) -> 35 | db:save(?LOC, X), 36 | io:format("swap books died!"), 37 | ok. 38 | handle_info(_, X) -> {noreply, X}. 39 | handle_cast({add, MID, S, Nonce, CID1, Type1, CID2, Type2}, X) -> 40 | M2 = case dict:find(MID, X) of 41 | error -> #market{orders = [S], mid = MID, 42 | cid1 = CID1, type1 = Type1, 43 | cid2 = CID2, type2 = Type2 44 | }; 45 | {ok, M} -> 46 | L2 = merge(S, M#market.orders), 47 | M#market{orders = L2, 48 | nonce = Nonce} 49 | end, 50 | X2 = dict:store(MID, M2, X), 51 | {noreply, X2}; 52 | handle_cast({garbage, Height}, X) -> 53 | X2 = garbage(X, Height), 54 | {noreply, X2}; 55 | handle_cast(_, X) -> {noreply, X}. 56 | handle_call({read, MID}, _From, X) -> 57 | Z = dict:find(MID, X), 58 | {reply, Z, X}; 59 | handle_call(markets2, _From, X) -> 60 | Y = lists:map( 61 | fun(Z) -> 62 | {ok, Market} = dict:find(Z, X), 63 | %Market#market{orders = 0}, 64 | #market{cid1 = CID1, cid2 = CID2} = Market, 65 | T1 = scalar_contracts:read_contract(CID1), 66 | T2 = scalar_contracts:read_contract(CID2), 67 | R1 = case T1 of 68 | {ok, Text1} -> Text1; 69 | _ -> "" 70 | end, 71 | R2 = case T2 of 72 | {ok, Text2} -> Text2; 73 | _ -> "" 74 | end, 75 | {Market#market{orders = 0}, 76 | R1, R2} 77 | end, 78 | dict:fetch_keys(X)), 79 | {reply, Y, X}; 80 | handle_call(markets, _From, X) -> 81 | Y = lists:map( 82 | fun(Z) -> 83 | {ok, Market} = dict:find(Z, X), 84 | Market#market{orders = 0} 85 | end, 86 | dict:fetch_keys(X)), 87 | {reply, Y, X}; 88 | handle_call({using_contract, CID}, _From, X) -> 89 | %checks if we are using this contract. scans all the markets. 90 | K = dict:fetch_keys(X), 91 | B = using_contract_internal(CID, X, K), 92 | {reply, B, X}; 93 | handle_call(_, _From, X) -> {reply, X, X}. 94 | 95 | using_contract_internal(_, _, []) -> 96 | false; 97 | using_contract_internal(CID, X, [H|T]) -> 98 | M = dict:fetch(H, X), 99 | if 100 | M#market.cid1 == CID -> true; 101 | M#market.cid2 == CID -> true; 102 | true -> 103 | using_contract_internal(CID, X, T) 104 | end. 105 | 106 | using_contract(X) -> 107 | gen_server:call(?MODULE, {using_contract, X}). 108 | 109 | add(MID, TID, Price, Amount, Nonce, CID1, Type1, CID2, Type2) -> 110 | S = new_order(TID, Price, Amount), 111 | gen_server:cast(?MODULE, {add, MID, S, Nonce, CID1, Type1, CID2, Type2}). 112 | read(ID) -> 113 | gen_server:call(?MODULE, {read, ID}). 114 | markets() -> 115 | gen_server:call(?MODULE, markets). 116 | markets2() -> 117 | gen_server:call(?MODULE, markets2). 118 | garbage() -> 119 | FN = utils:server_url(external), 120 | X = talker:talk({height}, FN), 121 | case X of 122 | {ok, Height} -> 123 | gen_server:cast(?MODULE, {garbage, Height}); 124 | _ -> ok 125 | end. 126 | 127 | garbage(D, Height) -> 128 | % FN = utils:server_url(external), 129 | % X = talker:talk({height}, FN), 130 | % case X of 131 | % {ok, Height} -> 132 | K = dict:fetch_keys(D), 133 | garbage2(K, D, Height). 134 | % _ -> D 135 | % end. 136 | garbage2([], D, _) -> D; 137 | garbage2([MID|T], D, Height) -> 138 | Market = dict:fetch(MID, D), 139 | Orders = Market#market.orders, 140 | Orders2 = garbage_orders(Orders, MID, Height), 141 | D2 = if 142 | ([] == Orders2) -> 143 | dict:erase(MID, D); 144 | true -> 145 | Market2 = 146 | Market#market{ 147 | orders = Orders2}, 148 | dict:store(MID, Market2, D) 149 | end, 150 | garbage2(T, D2, Height). 151 | garbage_orders([], _, _) -> []; 152 | garbage_orders([H|T], MID, Height) -> 153 | TID = H#order.tid, 154 | F = case swap_full:read(TID) of 155 | error -> []; 156 | {ok, {S, Second}} -> 157 | Offer = element(2, S), 158 | B = swap_verify:keep_longer( 159 | Offer, Height, TID), 160 | if 161 | (B == true) -> [H]; 162 | (B == already_accepted) -> 163 | if 164 | not(Second == 0) -> 165 | http_handler:doit({add, Second, 0}); 166 | true -> ok 167 | end, 168 | []; 169 | true -> 170 | [] 171 | % swap_history:remove(MID, TID), 172 | % case Second of 173 | % 0 -> ok; 174 | % _ -> re_absorb_cron( 175 | % Second, Height) 176 | % end, 177 | % swap_full:remove(TID), 178 | % [] 179 | end 180 | end, 181 | F ++ garbage_orders(T, MID, Height). 182 | 183 | 184 | merge(S, []) -> [S]; 185 | merge(S, [H|T]) -> 186 | if 187 | S#order.price > H#order.price -> 188 | [S|[H|T]]; 189 | true -> 190 | [H|merge(S, T)] 191 | end. 192 | 193 | 194 | garbage_cron() -> 195 | spawn(fun() -> 196 | timer:sleep(?cron), 197 | garbage() 198 | end), 199 | spawn(fun() -> 200 | timer:sleep(?cron), 201 | timer:sleep(?cron), 202 | garbage_cron() 203 | end). 204 | 205 | 206 | 207 | re_absorb_cron(SignedOffer, Height) -> 208 | FN = utils:server_url(external), 209 | spawn(fun()-> 210 | re_absorb_cron2( 211 | SignedOffer, Height) 212 | end). 213 | %re_absorb_cron_per_block(SignedOffer, Start, Recent) -> 214 | % %If the first offer is accepted, then we add the second to the pool. 215 | % FN = utils:server_url(external), 216 | % {ok, HeightNow} = talker:talk({height}, FN), 217 | % Offer = element(2, SignedOffer), 218 | % TID = utils:trade_id(Offer), 219 | % if 220 | % (HeightNow > (Start + 2)) -> 221 | % %out of time. try adding one last time. 222 | % http_handler:doit({add, SignedOffer, 0}); 223 | % %swap_verify:doit(TID, SignedOffer, 224 | % HeightNow); 225 | % (HeightNow == Recent) -> 226 | % %wait longer for the next block. 227 | % timer:sleep(?cron), 228 | % re_absorb_cron_per_block( 229 | % SignedOffer, Start, Recent); 230 | % (HeightNow > Recent) -> 231 | % %maybe we can add it now. 232 | % B = swap_verify:keep_longer(Offer, Height2, TID), 233 | % if 234 | % B -> 235 | % http_handler:doit({add, SignedOffer, 0}); 236 | % % swap_verify:doit( 237 | % TID, SignedOffer, HeightNow); 238 | % true -> 239 | % timer:sleep(?cron), 240 | % re_absorb_cron_per_block( 241 | % SignedOffer, Start, HeightNow) 242 | % end 243 | % end. 244 | 245 | re_absorb_cron2(SignedOffer, Height1) -> 246 | %for 2 blocks, keep trying to re-add this offer to the order book. 247 | io:fwrite("re absorb cron\n"), 248 | FN = utils:server_url(external), 249 | {ok, Height2} = talker:talk({height}, FN), 250 | Offer = element(2, SignedOffer), 251 | TID = utils:trade_id(Offer), 252 | B = swap_verify:keep_longer(Offer, Height2, TID), 253 | if 254 | Height2 > (Height1 + 2) -> 255 | io:fwrite("re absorb cron deleted\n"), 256 | %deleting the 2nd offer. 257 | ok; 258 | B -> 259 | io:fwrite("re absorb cron succeeded\n"), 260 | http_handler:doit({add, SignedOffer, 0}); 261 | true -> 262 | timer:sleep(?cron), 263 | re_absorb_cron2(SignedOffer, Height1) 264 | end. 265 | 266 | --------------------------------------------------------------------------------