%% @doc rq: Decentralized Identity Namespace Disambiguation Utility -module(rq). -vsn("0.1.0"). -author("Peter Harpending"). -copyright("Peter Harpending"). -export([start/1]). -compile([export_all,nowarn_export_all]). % TODO: rename "decode" to either "decompose", "compost" or "debug" % deploy opts -record(rdo, {deploy_file = "rq.deploy" :: file:filename(), op = deploy :: deploy | tx | signed_tx}). -spec start(ArgV) -> ok when ArgV :: [string()]. start(ArgV) -> ok = hz(), _ = application:ensure_all_started(ssl), rq(ArgV). help_screen() -> """ rq = Decentralized Identity Namespace Disambiguation Utility version 0.0.0 + 0.0.1i USAGE: rq [-s | --shell] [ARGS] equivalent to `rq ARGS`, but starts erlang shell with rq&deps in context; with no args, simply starts erlang shell. rq [-h | --help] show this menu rq debug show variables rq addr names rq addr names.aes rq ctstr names rq ctstr names.aes deployed address of names rq decode cb_... deep decode of api string but only the ones i use rq decode tx_... deep decode of api string but only the ones i use rq deploy rq deploy --tx debug ContractCreateTx rq deploy --tx-only rq deploy --tx_only rq deploy --signed debug SignedTx rq deploy --signed-tx rq deploy --signed_tx rq grids URL only works with the faucet grids rq my rq my everything all rq my akstr my akstr rq my acc rq my balance rq my explorer rq my explorer_link rq my explorer_url rq my url rq my rbx_tuples rq my tuples my rbx_tuples rq query ak_... hz:acc("ak_...") rq query ct_... hz:contract("ak_...") rq query th_... hz:contract("ak_...") rq lookup NAME rq register NAME rq src names.aes print contract source rq src rubicon.aes print contract source """. rq([]) -> rq_shell([]); rq(["-s" | Rest]) -> rq_shell(Rest); rq(["--shell" | Rest]) -> rq_shell(Rest); rq(Rest) -> rq_noshell(Rest). rq_shell(Args) -> ok = shell:start_interactive(), case Args of [] -> ok; More -> rq_II(More) end. rq_noshell(Args) -> rq_II(Args), zx:silent_stop(). %% @private %% dispatch function rq_II(Args) -> case Args of [Addr, Names] when ((Addr =:= "addr") orelse Addr =:= "ctstr") andalso (Names =:= "names.aes" orelse Names =:= "names") -> io:format("~ts~n", [nct_ctstr()]); ["debug" | Rest] -> rq_debug(Rest); ["decode", ApiStr] -> rq_decode(ApiStr); ["deploy"] -> rq_deploy(#rdo{}); ["deploy", "--signed"] -> rq_deploy(#rdo{op = signed_tx}); ["deploy", "--signed-tx"] -> rq_deploy(#rdo{op = signed_tx}); ["deploy", "--signed_tx"] -> rq_deploy(#rdo{op = signed_tx}); ["deploy", "--tx"] -> rq_deploy(#rdo{op = tx}); ["deploy", "--tx-only"] -> rq_deploy(#rdo{op = tx}); ["deploy", "--tx_only"] -> rq_deploy(#rdo{op = tx}); ["grids", URL] -> rq_grids(URL); ["my"] -> rq_my(everything); ["my", "everything"] -> rq_my(everything); ["my", "acc"] -> rq_my(acc); ["my", "addr"] -> rq_my(akstr); ["my", "akstr"] -> rq_my(akstr); ["my", "balance"] -> rq_my(balance); ["my", "explorer"] -> rq_my(url); ["my", "explorer_link"] -> rq_my(url); ["my", "explorer_url"] -> rq_my(url); ["my", "url"] -> rq_my(url); ["my", "rbx_tuples"] -> rq_my(rbx_tuples); ["my", "tuples"] -> rq_my(rbx_tuples); ["lookup", Name] -> rq_lookup(Name); ["register", Name] -> rq_register(Name); ["query" | Args2] -> rq_query(Args2); ["src" | Args2] -> rq_src(Args2); ["-h" | Args2] -> rq_help(Args2); ["--help" | Args2] -> rq_help(Args2); BadArgs -> badargs(BadArgs) end. rq_deploy(Opts = #rdo{deploy_file = F}) -> case file:read_file(F) of {ok, B} -> io:format("already deployed brother~n~ts~n", [B]), error(low_iq); _Error -> rq_deploy_II(Opts) end. rq_deploy_II(#rdo{op = Op}) -> {ok, CreateTx} = hz:contract_create(my_akstr(), rct_filepath(), {erlang, [my_rbx_tuples()]}), case Op of deploy -> sign_and_post(CreateTx); tx -> rq_decode(CreateTx); signed_tx -> Signed = hz:sign_tx(CreateTx, my_secret_key()), rq_decode(Signed); NYI -> io:format("NYI: ~tp~n", [NYI]), error(ur_mom_is_an_op) end. sign_and_post(UnsignedTx) -> SignedTx = hz:sign_tx(UnsignedTx, my_secret_key()), {ok, PostResult} = hz:post_tx(SignedTx), case PostResult of #{"tx_hash" := ThStr} -> rq_query([ThStr]); #{"reason" := NodeErr} -> io:format("ERROR: node sent back the following extremely helpful error message~n", []), io:format("~tp~n", [NodeErr]), error(NodeErr); _ -> io:format("ERROR (nyi) node sent back voodoo:~n", []), io:format("PostResult = ~tp~n", [PostResult]), error(nyi) end. rq_decode(TxStr = "tx_" ++ _) -> rq_decode(unicode:characters_to_binary(TxStr)); rq_decode(TxBin = <<"tx_", _/binary>>) -> io:format("TxBin = ~tp~n", [TxBin]), {transaction, Bytes} = gmser_api_encoder:decode(TxBin), just_the_tip_decode(Bytes), ok; rq_decode(CbStr = "cb_" ++ _) -> rq_decode(unicode:characters_to_binary(CbStr)); rq_decode(CbBin = <<"cb_", _/binary>>) -> io:format("CbBin = ~tp~n", [CbBin]), {contract_bytearray, Bytes} = gmser_api_encoder:decode(CbBin), io:format("Bytes = ~tp~n", [Bytes]), Deser = gmb_fate_encoding:deserialize(Bytes), io:format("Deserialized = ~tp~n", [Deser]), Fmt = gmb_fate_data:format(Deser), io:format("Formatted = ~ts~n", [Fmt]), ok. % this is like rq_decode but in the case where the data at hand is % NOT api-encoded; a typical example would be debugging a signed % transaction; one of the fields is the signatee, which needs to be % decomposed just_the_tip_decode(Bytes) -> io:format("Bytes = ~tp~n", [Bytes]), RLP = gmser_rlp:decode(Bytes), io:format("RLP = ~tp~n", [RLP]), [TagBin, VsnBin | Fields] = RLP, Tag = binary:decode_unsigned(TagBin), RevTag = rev_tag(Tag), Vsn = binary:decode_unsigned(VsnBin), io:format("Tag = ~tp~n", [Tag]), io:format("RevTag = ~tp~n", [RevTag]), io:format("Vsn = ~tp~n", [Vsn]), io:format("Fields = ~tp~n", [Fields]), Tpl = template(RevTag, Vsn), io:format("Template = ~tp~n", [Tpl]), NFields = gmser_chain_objects:deserialize(RevTag, Vsn, Tpl, Bytes), io:format("NamedFields = ~tp~n", [NFields]), deep_decode(RevTag, Vsn, Fields). deep_decode(signed_tx, 1, [Sigs, Tx]) -> [sig_valid(my_pubkey32(), Sig, Tx) || Sig <- Sigs], just_the_tip_decode(Tx), ok; deep_decode(contract_create_tx, 1, _) -> error(nyi); deep_decode(_, _, _) -> error(nyi). sig_valid(Pk32, Sig, Tx) -> io:format("Pk32 = ~tp~n", [Pk32]), io:format("Sig = ~tp~n", [Sig]), io:format("Tx = ~tp~n", [Tx]), NetworkId = <<"groot.testnet">>, {ok, Hash} = eblake2:blake2b(32, Tx), Signatee = <>, io:format("NetworkId = ~tp~n", [NetworkId]), io:format("Hash = ~tp~n", [Hash]), io:format("Signatee = ~tp~n", [Signatee]), SigValid = ecu_eddsa:sign_verify_detached(Sig, Signatee, Pk32), io:format("SigValid = ~tp~n", [SigValid]), ok. % hacky to get fund via faucet to work rq_grids(URL) -> {ok, {{sign, https}, URL_II}} = hz_grids:parse(URL), case httpc:request(URL_II) of {ok, {{_HttpVsn, 200, _Slogan}, _Hdrs, JSONBodyStr}} -> rq_grids_ii00(URL_II, JSONBodyStr); {ok, {{_HttpVsn, 404, _Slogan}, _Hdrs, _JSONBodyStr}} -> io:format("BUTTER YOUR OWN BISCUIT FATASS~n", []), io:format("(404... probably faucet copypasta expired)~n", []); Error -> io:format("ERROR: ~tp~n", [Error]) end. rq_grids_ii00(URL_II, JSONBodyStr) -> {ok, Decoded} = zj:decode(JSONBodyStr), io:format("Decoded = ~tp~n", [Decoded]), case Decoded of #{"chain" := "gajumaru", "grids" := 1, "network_id" := "groot.testnet", "payload" := PayloadStr, "public_id" := false, "signature" := undefined, "type" := "message"} -> rq_grids_iii(URL_II, Decoded, PayloadStr); _Nah -> error(nah_brah) end. rq_grids_iii(URL_II, Decoded, PayloadStr) -> io:format("PayloadStr = ~tp~n", [PayloadStr]), Sig = base64:encode(hz:sign_message(unicode:characters_to_binary(PayloadStr), my_secret_key())), Recoded = Decoded#{"signature" => Sig, "public_id" => my_akstr()}, io:format("Recoded = ~tp~n", [Recoded]), Reconfuckulated = zj:encode(Recoded), io:format("Reconfuckulated = ~tp~n", [Reconfuckulated]), Shit = httpc:request(post, {URL_II, [], "application/json", Reconfuckulated}, [], []), io:format("Shit = ~tp~n", [Shit]). % ok. rq_my(everything) -> rq_my(akstr), rq_my(rbx_tuples); rq_my(acc) -> Ak = my_akstr(), rq_query([Ak]); rq_my(balance) -> Ak = my_akstr(), {ok, #{"balance" := B}} = hz:acc(Ak), io:format("Balance = ~tp~n", [B]), io:format("log10(Balance) = ~tp~n", [math:log10(B)]); rq_my(akstr) -> io:format("~ts~n", [my_akstr()]); rq_my(url) -> io:format("https://groot.testnet.gajumaru.io/account/~ts~n", [my_akstr()]); rq_my(rbx_tuples) -> io:format("~tp~n", [my_rbx_tuples()]). rq_query([Ak = "ak_" ++ _ | Rest]) -> {ok, X} = hz:acc(Ak), io:format("~tp~n", [X]), rq_query(Rest); rq_query([Ct = "ct_" ++ _ | Rest]) -> {ok, X} = hz:contract(Ct), io:format("~tp~n", [X]), rq_query(Rest); rq_query([ThStr = "th" ++ _ | Rest]) -> io:format("ThStr = ~tp~n", [ThStr]), io:format("ExplorerUrl = ~tp~n", ["null"]), {ok, TxInfo} = hz:tx_info(ThStr), #{"call_info" := #{"return_value" := CbStr}} = TxInfo, rq_decode(CbStr), rq_query(Rest); rq_query([]) -> ok. rq_src(["names.aes"]) -> {ok, Bytes} = file:read_file(nct_filepath()), io:format("~s", [Bytes]); rq_src(["rubicon.aes"]) -> {ok, Bytes} = file:read_file(nct_filepath()), io:format("~s", [Bytes]); rq_src(FailArgs) -> badargs(FailArgs). badargs(FailArgs) -> io:format("you are a failure: ~p~n", [FailArgs]), rq_help([]), error(badargs). rq_help(_) -> io:format("~ts~n", [help_screen()]). rq_debug(_) -> ok = dbg_hz(), ok = dbg_my(), ok = dbg_nct(), ok = dbg_rct(), ok. dbg_hz() -> io:format("hz status: ~tp~n", [hz:status()]). dbg_my() -> Format = """ MY: akstr ~ts~n """, Args = [my_akstr()], io:format(Format, Args). dbg_nct() -> Format = """ NAMES CT: filepath ~tp ctstr ~ts aaci ~tp~n """, Args = [ nct_filepath() , nct_ctstr() , nct_aaci() ], io:format(Format, Args). dbg_rct() -> Format = """ RUBICON CT: filepath ~tp aaci ~tp~n """, Args = [ rct_filepath() , rct_aaci() ], io:format(Format, Args). rq_register(Name) -> ct_call(nct_aaci(), nct_ctstr(), "register", [Name]). rq_lookup(Name) -> ct_call(nct_aaci(), nct_ctstr(), "lookup", [Name]). ct_call(AACI, CtStr, FunName, ErlArgs) -> {ok, CallTx} = hz:contract_call(my_akstr(), AACI, CtStr, FunName, {erlang, ErlArgs}), SignedTx = hz:sign_tx(CallTx, my_secret_key()), {ok, PostResult} = hz:post_tx(SignedTx), case PostResult of #{"tx_hash" := ThStr} -> rq_query(ThStr); #{"error" := NodeErr} -> io:format("ERROR: node sent back the following extremely helpful error message~n", []), io:format("NodeErr = ~tp~n", [NodeErr]), error(NodeErr); _ -> io:format("ERROR (nyi) node sent back voodoo:~n", []), io:format("PostResult = ~tp~n", [PostResult]), error(nyi) end. %% name contract deployed address nct_ctstr() -> "ct_2CDom5hKimtKSsTykhBYBKMgGv7youakZPVgk5h9qgNL3P65oV". nct_aaci() -> case hz:prepare_contract(nct_filepath()) of {ok, AACI} -> AACI; {error, E} -> error(E) end. nct_filepath() -> ct_dir() ++ "/names.aes". %% rubicon contract deployed address rct_aaci() -> case hz:prepare_contract(rct_filepath()) of {ok, AACI} -> AACI; {error, E} -> error(E) end. rct_filepath() -> ct_dir() ++ "/rubicon.aes". ct_dir() -> zx:get_home() ++ "/ct". %% my keys % "ak_2pr2BL1jK625Uedt888p3YKRUEk3GDTo5r2zKW6gMaZePQP5ou" my_akstr() -> unicode:characters_to_list(gmser_api_encoder:encode(account_pubkey, my_pubkey32())). my_pubkey32() -> maps:get(public, my_keypair()). my_secret_key() -> maps:get(secret, my_keypair()). my_keypair() -> ecu_eddsa:sign_seed_keypair(my_seed()). my_seed() -> crypto:hash(sha3_256, <<"p@ssw0rd">>). my_rbx_tuples() -> NoiseStr = "Noise_NK_25519_ChaChaPoly_BLAKE2b", ServicePubkey = <<116, 159, 91, 248, 138, 250, 73, 40, 231, 50, 81, 110, 137, 163, 44, 76, 48, 130, 225, 95, 168, 121, 93, 44, 148, 42, 180, 103, 11, 40, 168, 96>>, ServiceAkStr = gmser_api_encoder:encode(account_pubkey, ServicePubkey), NoisePrologue = <<>>, [{"127.0.0.1", 6969, "tcp", NoiseStr, ServiceAkStr, NoisePrologue}]. %% hz setup hz() -> ok = application:ensure_started(hakuzaru), ok = hz:tls(false), ok = hz:chain_nodes(hz_nodes()). hz_nodes() -> [ {"tsuriai.jp", 4013} , {"groot.testnet.gajumaru.io", 3013} ]. %% deploying deploy_rct_with(RbxTuples) -> case hz:contract_create(my_akstr(), rct_filepath(), [RbxTuples]) of {ok, CreateTx} -> deploy_rct_II(CreateTx); Error -> io:format("you are a failure: ~tp~n", [Error]), error(Error) end. deploy_rct_II(UnsignedCreateTx) -> SecretKey = my_secret_key(), case hz:sign_tx(UnsignedCreateTx, SecretKey) of {ok, SignedTx} -> deploy_rct_III(SignedTx); Error -> io:format("you are a failure II: ~tp~n", [Error]), error(Error) end. deploy_rct_III(SignedCreateTx) -> case hz:post_tx(SignedCreateTx) of {ok, Result_III} -> io:format("Result the third: ~tp~n", [Result_III]); Error -> io:format("you are a failure III: ~tp~n", [Error]), error(Error) end. %% totally not copypasted from gmser_chain_objects.erl rev_tag(10) -> account; rev_tag(11) -> signed_tx; rev_tag(12) -> spend_tx; rev_tag(13) -> data_extend_tx; rev_tag(30) -> name; rev_tag(31) -> name_commitment; rev_tag(32) -> name_claim_tx; rev_tag(33) -> name_preclaim_tx; rev_tag(34) -> name_update_tx; rev_tag(35) -> name_revoke_tx; rev_tag(36) -> name_transfer_tx; rev_tag(37) -> name_auction; rev_tag(40) -> contract; rev_tag(41) -> contract_call; rev_tag(42) -> contract_create_tx; rev_tag(43) -> contract_call_tx; rev_tag(44) -> contract_source; rev_tag(50) -> channel_create_tx; rev_tag(501) -> channel_set_delegates_tx; rev_tag(502) -> channel_delegates; rev_tag(51) -> channel_deposit_tx; rev_tag(52) -> channel_withdraw_tx; rev_tag(521) -> channel_force_progress_tx; rev_tag(53) -> channel_close_mutual_tx; rev_tag(54) -> channel_close_solo_tx; rev_tag(55) -> channel_slash_tx; rev_tag(56) -> channel_settle_tx; rev_tag(57) -> channel_offchain_tx; rev_tag(570) -> channel_offchain_update_transfer; rev_tag(571) -> channel_offchain_update_deposit; rev_tag(572) -> channel_offchain_update_withdraw; rev_tag(573) -> channel_offchain_update_create_contract; rev_tag(574) -> channel_offchain_update_call_contract; rev_tag(576) -> channel_offchain_update_meta; rev_tag(575) -> channel_client_reconnect_tx; rev_tag(58) -> channel; rev_tag(59) -> channel_snapshot_solo_tx; rev_tag(60) -> trees_poi; rev_tag(61) -> trees_db; rev_tag(62) -> state_trees; rev_tag(63) -> mtree; rev_tag(64) -> mtree_value; rev_tag(621) -> contracts_mtree; rev_tag(622) -> calls_mtree; rev_tag(623) -> channels_mtree; rev_tag(624) -> nameservice_mtree; rev_tag(626) -> accounts_mtree; rev_tag(627) -> acs_mtree; rev_tag(628) -> entries_mtree; rev_tag(70) -> compiler_sophia; rev_tag(80) -> ga_attach_tx; rev_tag(81) -> ga_meta_tx; rev_tag(82) -> paying_for_tx; rev_tag(810) -> ga_meta_tx_auth_data; rev_tag(90) -> associate_chain; rev_tag(91) -> ac_state; rev_tag(92) -> ac_burn; rev_tag(93) -> ac_create_tx; rev_tag(94) -> ac_deposit_tx; rev_tag(95) -> ac_update_cops_tx; rev_tag(96) -> ac_rollup_tx; rev_tag(97) -> ac_proposal_tx; rev_tag(100) -> key_block; rev_tag(101) -> micro_block; rev_tag(102) -> light_micro_block; rev_tag(110) -> testimony; rev_tag(111) -> testimony_tx; rev_tag(120) -> nt_native_token; rev_tag(121) -> nt_create_tx; rev_tag(122) -> nt_mint_tx; rev_tag(123) -> nt_finalize_tx; rev_tag(124) -> nt_trade_tx; rev_tag(125) -> nt_burn_tx; rev_tag(140) -> entry; rev_tag(141) -> entry_create_tx; rev_tag(142) -> entry_transfer_tx; rev_tag(143) -> entry_destroy_tx; rev_tag(200) -> pof. tag(account) -> 10; tag(signed_tx) -> 11; tag(spend_tx) -> 12; tag(data_extend_tx) -> 13; tag(name) -> 30; tag(name_commitment) -> 31; tag(name_claim_tx) -> 32; tag(name_preclaim_tx) -> 33; tag(name_update_tx) -> 34; tag(name_revoke_tx) -> 35; tag(name_transfer_tx) -> 36; tag(name_auction) -> 37; tag(contract) -> 40; tag(contract_call) -> 41; tag(contract_create_tx) -> 42; tag(contract_call_tx) -> 43; tag(contract_source) -> 44; tag(channel_create_tx) -> 50; tag(channel_set_delegates_tx) -> 501; tag(channel_delegates) -> 502; tag(channel_deposit_tx) -> 51; tag(channel_withdraw_tx) -> 52; tag(channel_force_progress_tx) -> 521; tag(channel_close_mutual_tx) -> 53; tag(channel_close_solo_tx) -> 54; tag(channel_slash_tx) -> 55; tag(channel_settle_tx) -> 56; tag(channel_offchain_tx) -> 57; tag(channel_offchain_update_transfer) -> 570; tag(channel_offchain_update_deposit) -> 571; tag(channel_offchain_update_withdraw) -> 572; tag(channel_offchain_update_create_contract) -> 573; tag(channel_offchain_update_call_contract) -> 574; tag(channel_offchain_update_meta) -> 576; tag(channel_client_reconnect_tx) -> 575; tag(channel) -> 58; tag(channel_snapshot_solo_tx) -> 59; tag(trees_poi) -> 60; tag(trees_db) -> 61; tag(state_trees) -> 62; tag(mtree) -> 63; tag(mtree_value) -> 64; tag(contracts_mtree) -> 621; tag(calls_mtree) -> 622; tag(channels_mtree) -> 623; tag(nameservice_mtree) -> 624; tag(accounts_mtree) -> 626; tag(acs_mtree) -> 627; tag(entries_mtree) -> 628; tag(compiler_sophia) -> 70; tag(ga_attach_tx) -> 80; tag(ga_meta_tx) -> 81; tag(paying_for_tx) -> 82; tag(ga_meta_tx_auth_data) -> 810; tag(associate_chain) -> 90; tag(ac_state) -> 91; tag(ac_burn) -> 92; tag(ac_create_tx) -> 93; tag(ac_deposit_tx) -> 94; tag(ac_update_cops_tx) -> 95; tag(ac_rollup_tx) -> 96; tag(ac_proposal_tx) -> 97; tag(key_block) -> 100; tag(micro_block) -> 101; tag(light_micro_block) -> 102; tag(testimony) -> 110; tag(testimony_tx) -> 111; tag(nt_native_token) -> 120; tag(nt_create_tx) -> 121; tag(nt_mint_tx) -> 122; tag(nt_finalize_tx) -> 123; tag(nt_trade_tx) -> 124; tag(nt_burn_tx) -> 125; tag(entry) -> 140; tag(entry_create_tx) -> 141; tag(entry_transfer_tx) -> 142; tag(entry_destroy_tx) -> 143; tag(pof) -> 200. template(signed_tx, 1) -> [{sigs, [binary]}, {tx, binary}]; template(contract_create_tx, 1) -> [{owner_id, id}, {nonce, int}, {code, binary}, {source, binary}, {ct_version, int}, {ttl, int}, {deposit, int}, {amount, int}, {gas_price, int}, {gas, int}, {call_data, binary}]; template(_, _) -> error(nyi).