Compare commits
79 Commits
e10ef8a460
...
v0.9.1
| Author | SHA1 | Date | |
|---|---|---|---|
| da92d80334 | |||
| f821d57c1c | |||
| e87be689a8 | |||
| 2a7de4fee1 | |||
| 82d08da8ca | |||
| 85d0c6fd04 | |||
| d8221e0b25 | |||
| b950bb8a67 | |||
| a4914c1ad1 | |||
| 9e6d9ec02e | |||
| 4b9fa65672 | |||
| 74aaad297a | |||
| c9ead44aa2 | |||
| c54c0db17a | |||
| cd4f6a56a5 | |||
| fd2158a465 | |||
| 7fc3cd00da | |||
| 02945dd10d | |||
| 695e7e4828 | |||
| 9f02f73dbd | |||
| fd8766a249 | |||
| 540b2c513b | |||
| bda4e89e58 | |||
| f277e79096 | |||
| ddec3bfa74 | |||
| a0fbeebcdb | |||
| 78c9c67f38 | |||
| 9bc0ffafd1 | |||
| a1fc5f19fa | |||
| efe0a64056 | |||
| 60985130cb | |||
| 6c172c4783 | |||
| 3838a7e3c5 | |||
| d014ae0982 | |||
| bb4bcbb7de | |||
| a695c21fc9 | |||
| 493bdb990c | |||
| 17f635af61 | |||
| 272ed01fdc | |||
| 49cd8b6687 | |||
| 966b4b2748 | |||
| fe182a5233 | |||
| f1696e2b9e | |||
| 2bf384ca82 | |||
| 4f2a3c6c6f | |||
| 7df04a81be | |||
| 6f02d4c4e6 | |||
| 48bcccdf23 | |||
| 03b9756066 | |||
| 56e63051bc | |||
| 3f1c9bd626 | |||
| 97e32574c4 | |||
| 6f5525afcf | |||
| 4f1958b210 | |||
| 3da9bd570b | |||
| d65a048409 | |||
| 9280495b18 | |||
| d2163c1ff8 | |||
| af1639d47b | |||
| cb36bad74b | |||
| 17a2b867fe | |||
| a35118db7a | |||
| b719c946ef | |||
| 8b6085dee8 | |||
| 60c8adb4b1 | |||
| ebb84b39a1 | |||
| e456a96f52 | |||
| 2f93c4d503 | |||
| b542205c0e | |||
| 6ba0c1f2ae | |||
| d1cae68ce7 | |||
| ccd9a8c83d | |||
| f5e955b583 | |||
| 61984d1529 | |||
| 1978ca59b3 | |||
| 4ee6609111 | |||
| aeb78eab38 | |||
| 88c6f6dcc7 | |||
| d5ff77b278 |
+2
-2
@@ -8,9 +8,9 @@ cancer
|
|||||||
erl_crash.dump
|
erl_crash.dump
|
||||||
ebin/*.beam
|
ebin/*.beam
|
||||||
doc/*.html
|
doc/*.html
|
||||||
doc/*.css
|
|
||||||
doc/edoc-info
|
|
||||||
doc/erlang.png
|
doc/erlang.png
|
||||||
|
doc/stylesheet.css
|
||||||
|
doc/edoc-info
|
||||||
rel/example_project
|
rel/example_project
|
||||||
.concrete/DEV_MODE
|
.concrete/DEV_MODE
|
||||||
.rebar
|
.rebar
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
@@ -0,0 +1,75 @@
|
|||||||
|
/* standard EDoc style sheet */
|
||||||
|
body {
|
||||||
|
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||||
|
margin-left: .25in;
|
||||||
|
margin-right: .2in;
|
||||||
|
margin-top: 0.2in;
|
||||||
|
margin-bottom: 0.2in;
|
||||||
|
color: #696969;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
a:link{
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
a:visited{
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
a:hover{
|
||||||
|
color: #d8613c;
|
||||||
|
}
|
||||||
|
h1,h2 {
|
||||||
|
margin-left: -0.2in;
|
||||||
|
}
|
||||||
|
div.navbar {
|
||||||
|
background-color: #000000;
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
h2.indextitle {
|
||||||
|
padding: 0.4em;
|
||||||
|
color: #dfdfdf;
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
div.navbar a:link {
|
||||||
|
color: #dfdfdf;
|
||||||
|
}
|
||||||
|
div.navbar a:visited {
|
||||||
|
color: #dfdfdf;
|
||||||
|
}
|
||||||
|
div.navbar a:hover {
|
||||||
|
color: #d8613c;
|
||||||
|
}
|
||||||
|
h3.function,h3.typedecl {
|
||||||
|
background-color: #000000;
|
||||||
|
color: #dfdfdf;
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
div.spec {
|
||||||
|
margin-left: 2em;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
a.module {
|
||||||
|
text-decoration:none
|
||||||
|
}
|
||||||
|
a.module:hover {
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
ul.definitions {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
ul.index {
|
||||||
|
list-style-type: none;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Minor style tweaks
|
||||||
|
*/
|
||||||
|
ul {
|
||||||
|
list-style-type: square;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 3
|
||||||
|
}
|
||||||
+59
-57
@@ -1,77 +1,79 @@
|
|||||||
@author Craig Everett <ceverett@zxq9.com> [https://gitlab.com/zxq9/zj]
|
@author Craig Everett <craigeverett@qpq.swiss> [https://git.qpq.swiss/QPQ-AG/hakuzaru]
|
||||||
@version 0.3.0
|
@version 0.9.1
|
||||||
@title Vanillae: Aeternity blockchain bindings for Erlang
|
@title Hakuzaru: Gajumaru blockchain bindings for Erlang
|
||||||
|
|
||||||
@doc
|
@doc
|
||||||
This Erlang application provides bindings for the Erlang blockchain.
|
This Erlang application provides bindings for the Gajumaru blockchain and basic utilities for manipulating Gajumaru-related data.
|
||||||
The primary goal is for usage to be easy to understand and as simple as possible to use.
|
|
||||||
The secondary goal is to enable real-world projects to more easily connect with the blockchain in an obvious way and provide a clear path for them to provide feedback regarding areas that are difficult to understand, functionality that is lacking, and explain their use cases to us so we can more easily provide needed features and usage examples to make adoption easier.
|
|
||||||
|
|
||||||
== Basic operation ==
|
== To Start or Not To Start ==
|
||||||
All external interfaces expected to be used by authors of programs that use Vanillae are built into the `vanillae' module.
|
Starting the `hakuzaru' application is only required if you need to query the chain.
|
||||||
|
|
||||||
When Vanillae is started as an application a named process called `vanillae_man' is spawned that manages interactions with and the state of the service, as well as a simple-one-for-one supervisor that manages the lifecycle of Vanillae workers (defined in `vanillae_fetcher').
|
The application can be started via a call to `application', or with an explicit call to `hz:start()'.
|
||||||
|
|
||||||
After startup `vanillae_man' must be given the address and port of a list of Aeternity nodes that are available to service requests. Note that the service nodes will need to have the "dry run" endpoint enabled and the internal service query port made available in order to provide "dry run" and mempool TX submission functionality.
|
Hakuzaru can also be run as a local application from the shell by invoking it with `zxh run hakuzaru' if you have `zx' installed.
|
||||||
|
|
||||||
The `vanillae_man' will round-robin requests to however many Aeternity nodes are provided in its configuration. Note that this congiruation is dynamic and can be changed completely at runtime.
|
== Operation ==
|
||||||
|
All blockchain-specific operations are accessible from the main interface modulle: `hz'
|
||||||
|
|
||||||
== Functions ==
|
When Hakuzaru is started as an application a named process called `hz_man' is spawned that manages interactions with chain nodes, as well as a simple-one-for-one supervisor that manages the lifecycle of workers (defined in `hz_fetcher').
|
||||||
The `vanillae' module exposes one function per blockchain feature provided. Most of these are actually wrappers for blockchain endpoint functions, others provide functionality specific to accomplishing a local processing task related to chain data.
|
|
||||||
|
|
||||||
== Initialization ==
|
After startup `hz_man' must be given the address and port of a list of Gajumaru nodes that are available to service requests.
|
||||||
When Vanillae is first started the vanillae_man is started but does not yet know what Aeternity nodes to use to service queries. You will need to provide it with at least one node and port where it can make Aeternity endpoint calls.
|
Note that the service nodes will need to have the dry-run endpoint enabled and the internal service query port made available in order to provide dry-runs and transaction submission.
|
||||||
|
|
||||||
Note that if you will need to make read-only calls to contracts that are deployed on chain (to queery their state or perform specific read-only operations provided by the contract) the backend nodes you configure will need to be configured with "dry-run" enabled.
|
When configuring chain nodes a list of nodes should be provided.
|
||||||
|
To avoid sync issues in the case of fast transaction formation/submission to the chain, only one node from the list of chain nodes is used for submitting transactions and querying `next_nonce/1`.
|
||||||
|
This node is called "the sticky node".
|
||||||
|
|
||||||
Example of a shell session where vanillae is started and initialized manually with an AE node in the local network at 192.168.10.10:3013:
|
The first node in the list of chain nodes provided during configuration is designated as the sticky node.
|
||||||
|
If you also want to use the sticky node as a query endpoint, include it twice in the list.
|
||||||
|
|
||||||
|
The `hz_man' will round-robin requests to however many additional Gajumaru nodes are provided in the configuration.
|
||||||
|
Note that this configuration is dynamic and can be changed at runtime, so your service can adapt to node availability on the fly if needed.
|
||||||
|
|
||||||
```
|
```
|
||||||
1> vanillae:start().
|
ceverett@steak:~$ zxh run hakuzaru
|
||||||
Starting.
|
Erlang/OTP 27 [erts-15.2] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit:ns]
|
||||||
|
|
||||||
|
Eshell V15.2 (press Ctrl+G to abort, type help(). for help)
|
||||||
|
Fetching otpr-hakuzaru-0.8.0
|
||||||
|
[100.00%]
|
||||||
|
Recompile: src/hz_sup
|
||||||
|
Recompile: src/hz_man
|
||||||
|
Recompile: src/hz_key_master
|
||||||
|
Recompile: src/hz_grids
|
||||||
|
Recompile: src/hz_format
|
||||||
|
Recompile: src/hz_fetcher
|
||||||
|
Recompile: src/hz
|
||||||
|
Recompile: src/hakuzaru
|
||||||
|
Starting otpr-hakuzaru-0.8.0.
|
||||||
|
hz_man starting.
|
||||||
|
Started [hakuzaru]
|
||||||
|
1> hz:chain_nodes([{"groot.testnet.gajumaru.io", 3013}]).
|
||||||
ok
|
ok
|
||||||
2> vanillae:status().
|
2> hz:status().
|
||||||
{error,no_nodes}
|
{ok,#{"difficulty" => 2877405482,
|
||||||
3> vanillae:ae_nodes([{"192.168.7.7", 3013}]).
|
"finalized" =>
|
||||||
ok
|
#{"hash" =>
|
||||||
4> vanillae:status().
|
"kh_PDSn6Xru5JVdpJfdDCNpfsL8gUZvvjyhTYuzgndoy98G5oLLR",
|
||||||
{ok,#{"difficulty" => 59729882,
|
"height" => 277454,"type" => "height"},
|
||||||
"genesis_key_block_hash" =>
|
"genesis_key_block_hash" =>
|
||||||
"kh_wUCideEB8aDtUaiHCtKcfywU6oHZW6gnyci8Mw6S1RSTCnCRu",
|
"kh_Qdi5MTuuhJm7xzn5JUAbYG12cX3qoLMnXrBxPGzBkMWJ4K8vq",
|
||||||
"listening" => true,"network_id" => "ae_uat",
|
"hashrate" => 864394,"listening" => true,
|
||||||
|
"network_id" => "groot.testnet",
|
||||||
"node_revision" =>
|
"node_revision" =>
|
||||||
"3a08153c635c53d92029a617f2e784731ba367c6",
|
"7b3cc1db3bb36053023167b86f7d6f2d5dcbd01d",
|
||||||
"node_version" => "6.7.0",
|
"node_version" => "0.1.0+203.7b3cc1db3",
|
||||||
"peer_connections" => #{"inbound" => 25,"outbound" => 10},
|
"peer_connections" => #{"inbound" => 1,"outbound" => 3},
|
||||||
"peer_count" => 50,
|
"peer_count" => 5,
|
||||||
"peer_pubkey" =>
|
"peer_pubkey" =>
|
||||||
"pp_fCBqobeSwhdnrzC8DoSsmWbf2GzDK61CJujmsCEd3RUkmh9Ny",
|
"pp_2nQHucGyEt5wkYruNuRkg19cbZuEeyR9BZfvtv49F3AoyNSYMT",
|
||||||
"pending_transactions_count" => 2,
|
"pending_transactions_count" => 0,
|
||||||
"protocols" =>
|
"protocols" =>
|
||||||
[#{"effective_at_height" => 425900,"version" => 5},
|
[#{"effective_at_height" => 0,"version" => 1}],
|
||||||
#{"effective_at_height" => 154300,"version" => 4},
|
|
||||||
#{"effective_at_height" => 82900,"version" => 3},
|
|
||||||
#{"effective_at_height" => 40900,"version" => 2},
|
|
||||||
#{"effective_at_height" => 0,"version" => 1}],
|
|
||||||
"solutions" => 0,"sync_progress" => 100.0,
|
"solutions" => 0,"sync_progress" => 100.0,
|
||||||
"syncing" => false,"top_block_height" => 802644,
|
"syncing" => false,"top_block_height" => 277555,
|
||||||
|
"top_hash" =>
|
||||||
|
"kh_2vuNc8eG77aTmHcQDcievjKufFwR4MSSuZbEMWwW5TqUzSQy71",
|
||||||
"top_key_block_hash" =>
|
"top_key_block_hash" =>
|
||||||
"kh_28LZSvHZPCGqeWsMsqtSjxQjQHKW1pHzoBex97oMT7U2HcLPgV"}}
|
"kh_2vuNc8eG77aTmHcQDcievjKufFwR4MSSuZbEMWwW5TqUzSQy71"}}
|
||||||
'''
|
|
||||||
|
|
||||||
Alternatively, here is a start function for an application using Vanillae that initializes vanillae_man with a list of nodes provided by a configuration file:
|
|
||||||
|
|
||||||
```
|
|
||||||
start(normal, _Args) ->
|
|
||||||
ok = application:ensure_started(sasl),
|
|
||||||
{ok, Started} = application:ensure_all_started(cowboy),
|
|
||||||
ok = application:ensure_started(vanillae),
|
|
||||||
Nodes = proplists:get_value(ae_nodes, read_config(), []),
|
|
||||||
ok = vanillae:ae_nodes(Nodes),
|
|
||||||
ok = log(info, "Started: ~p~n", [[vanillae | Started]]),
|
|
||||||
Routes = [{'_', [{"/", count_top, []}]}],
|
|
||||||
Dispatch = cowboy_router:compile(Routes),
|
|
||||||
Env = #{env => #{dispatch => Dispatch}},
|
|
||||||
{ok, _} = cowboy:start_clear(count_listener, [{port, 8080}], Env),
|
|
||||||
count_sup:start_link().
|
|
||||||
'''
|
'''
|
||||||
|
|||||||
+3
-3
@@ -3,7 +3,7 @@
|
|||||||
{included_applications,[]},
|
{included_applications,[]},
|
||||||
{applications,[stdlib,kernel]},
|
{applications,[stdlib,kernel]},
|
||||||
{description,"Gajumaru interoperation library"},
|
{description,"Gajumaru interoperation library"},
|
||||||
{vsn,"0.7.0"},
|
{vsn,"0.9.1"},
|
||||||
{modules,[hakuzaru,hz,hz_fetcher,hz_grids,hz_key_master,hz_man,
|
{modules,[hakuzaru,hz,hz_aaci,hz_fetcher,hz_format,hz_grids,
|
||||||
hz_sup]},
|
hz_key_master,hz_man,hz_sophia,hz_sup]},
|
||||||
{mod,{hakuzaru,[]}}]}.
|
{mod,{hakuzaru,[]}}]}.
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hakuzaru).
|
-module(hakuzaru).
|
||||||
-vsn("0.7.0").
|
-vsn("0.9.1").
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
-author("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
|
|||||||
+246
-1170
File diff suppressed because it is too large
Load Diff
+1269
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
-module(hz_fetcher).
|
-module(hz_fetcher).
|
||||||
-vsn("0.7.0").
|
-vsn("0.9.1").
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
-author("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-license("MIT").
|
-license("MIT").
|
||||||
|
|||||||
+179
-94
@@ -21,13 +21,15 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_format).
|
-module(hz_format).
|
||||||
-vsn("0.7.0").
|
-vsn("0.9.1").
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
-author("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-license("GPL-3.0-or-later").
|
-license("GPL-3.0-or-later").
|
||||||
|
|
||||||
-export([amount/1, amount/2, amount/3, amount/4,
|
-export([amount/1, amount/2, amount/3,
|
||||||
|
approx_amount/2, approx_amount/3,
|
||||||
read/1,
|
read/1,
|
||||||
|
one/1, mark/1,
|
||||||
price_to_string/1, string_to_price/1]).
|
price_to_string/1, string_to_price/1]).
|
||||||
|
|
||||||
-spec amount(Pucks) -> Formatted
|
-spec amount(Pucks) -> Formatted
|
||||||
@@ -35,7 +37,20 @@
|
|||||||
Formatted :: string().
|
Formatted :: string().
|
||||||
%% @doc
|
%% @doc
|
||||||
%% A convenience formatting function.
|
%% A convenience formatting function.
|
||||||
%% @equiv amount(us, Pucks).
|
%% ```
|
||||||
|
%% hz_format:amount(1) ->
|
||||||
|
%% 木0.000,000,000,000,000,001
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(5000) ->
|
||||||
|
%% 木0.000,000,000,000,005
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(5000000000000000000) ->
|
||||||
|
%% 木5
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(500000123000000000000000) ->
|
||||||
|
%% 木500,000.123
|
||||||
|
%% '''
|
||||||
|
%% @equiv amount(us, Pucks)
|
||||||
|
|
||||||
amount(Pucks) ->
|
amount(Pucks) ->
|
||||||
amount(us, Pucks).
|
amount(us, Pucks).
|
||||||
@@ -49,7 +64,26 @@ amount(Pucks) ->
|
|||||||
Formatted :: string().
|
Formatted :: string().
|
||||||
%% @doc
|
%% @doc
|
||||||
%% A money formatting function.
|
%% A money formatting function.
|
||||||
%% @equiv amount(gaju, Style, Pucks).
|
%% ```
|
||||||
|
%% hz_format:amount(us, 100500040123000000000000000) ->
|
||||||
|
%% 木100,500,040.123
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(jp, 100500040123000000000000000) ->
|
||||||
|
%% 1億50万40木 12京3000兆本
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(metric, 100500040123000000000000000) ->
|
||||||
|
%% 木100m 500k 40 G 123p P
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(legacy, 100500040123000000000000000) ->
|
||||||
|
%% 木100m 500k 40 G 123q P
|
||||||
|
%%
|
||||||
|
%% hz_format:amount({$_, 3}, 100500040123000000000000000) ->
|
||||||
|
%% 木100_500_040.123
|
||||||
|
%%
|
||||||
|
%% hz_format:amount({$_, 4}, 100500040123000000000000000) ->
|
||||||
|
%% 木1_0050_0040.123
|
||||||
|
%% '''
|
||||||
|
%% @equiv amount(gaju, Style, Pucks)
|
||||||
|
|
||||||
amount(Style, Pucks) ->
|
amount(Style, Pucks) ->
|
||||||
amount(gaju, Style, Pucks).
|
amount(gaju, Style, Pucks).
|
||||||
@@ -64,6 +98,31 @@ amount(Style, Pucks) ->
|
|||||||
Formatted :: string().
|
Formatted :: string().
|
||||||
%% @doc
|
%% @doc
|
||||||
%% A simplified format function covering the most common formats desired.
|
%% A simplified format function covering the most common formats desired.
|
||||||
|
%% ```
|
||||||
|
%% hz_format:amount(gaju, us, 100500040123000004500000000) ->
|
||||||
|
%% 木100,500,040.123,000,004,5
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(puck, us, 100500040123000004500000000) ->
|
||||||
|
%% 本100,500,040,123,000,004,500,000,000
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(gaju, jp, 100500040123000004500000000) ->
|
||||||
|
%% 1億50万40木 12京3000兆45億本
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(puck, jp, 100500040123000004500000000) ->
|
||||||
|
%% 100秭5000垓4012京3000兆45億本
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(gaju, metric, 100500040123000004500000000) ->
|
||||||
|
%% 木100m 500k 40 G 123p 4g 500m P
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(puck, metric, 100500040123000004500000000) ->
|
||||||
|
%% 本100y 500z 40e 123p 4g 500m P
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(gaju, legacy, 100500040123000004500000000) ->
|
||||||
|
%% 木100m 500k 40 G 123q 4b 500m P
|
||||||
|
%%
|
||||||
|
%% hz_format:amount(puck, legacy, 100500040123000004500000000) ->
|
||||||
|
%% 本100y 500z 40e 123q 4b 500m P
|
||||||
|
%% '''
|
||||||
|
|
||||||
amount(gaju, us, Pucks) ->
|
amount(gaju, us, Pucks) ->
|
||||||
western($,, $., 3, all, Pucks);
|
western($,, $., 3, all, Pucks);
|
||||||
@@ -81,66 +140,51 @@ amount(puck, {Separator, Span}, Pucks) ->
|
|||||||
western(Separator, Span, Pucks).
|
western(Separator, Span, Pucks).
|
||||||
|
|
||||||
|
|
||||||
-spec amount(Unit, Style, Precision, Pucks) -> Serialized
|
|
||||||
when Unit :: gaju | puck,
|
-spec approx_amount(Precision, Pucks) -> Serialized
|
||||||
Style :: us | jp | metric | legacy | {Separator, Span},
|
when Precision :: all | 0..18,
|
||||||
|
Pucks :: integer(),
|
||||||
|
Serialized :: string().
|
||||||
|
%% A formatter for decimal notation which permits a precision
|
||||||
|
%% value to be applied to the puck side of the format.
|
||||||
|
%% ```
|
||||||
|
%% hz_format:approx_amount(3, 100500040123000004500000001) ->
|
||||||
|
%% 木100,500,040.123
|
||||||
|
%%
|
||||||
|
%% hz_format:approx_amount(13, 100500040123000004500000001) ->
|
||||||
|
%% 木100,500,040.123,000,004,5...
|
||||||
|
%%
|
||||||
|
%% hz_format:approx_amount(all, 100500040123000004500000001) ->
|
||||||
|
%% 木100,500,040.123,000,004,500,000,001
|
||||||
|
%% '''
|
||||||
|
%% @equiv approx_amount(us, Precision, Pucks)
|
||||||
|
|
||||||
|
approx_amount(Precision, Pucks) ->
|
||||||
|
approx_amount(us, Precision, Pucks).
|
||||||
|
|
||||||
|
|
||||||
|
-spec approx_amount(Style, Precision, Pucks) -> Serialized
|
||||||
|
when Style :: us | {Separator, Span},
|
||||||
Precision :: all | 0..18,
|
Precision :: all | 0..18,
|
||||||
Separator :: $, | $_,
|
Separator :: $, | $_,
|
||||||
Span :: 3 | 4,
|
Span :: 3 | 4,
|
||||||
Pucks :: integer(),
|
Pucks :: integer(),
|
||||||
Serialized :: string().
|
Serialized :: string().
|
||||||
%% @doc
|
%% @doc
|
||||||
%% A flexible, if annoyingly complex, formatting function.
|
%% A formatter for decimal notation which permits a precision
|
||||||
%%
|
%% value to be applied to the puck side of the format.
|
||||||
%% ```
|
%% ```
|
||||||
%% amount(gaju, us, 3, 123456789123456789123456789) ->
|
%% hz_format:approx_amount({$_, 3}, 3, 100500040123000004500000001) ->
|
||||||
%% "木123,456,789.123...".
|
%% 木100_500_040.123...
|
||||||
%%
|
%%
|
||||||
%% amount(gaju, us, 3, 123456789123000000000000000) ->
|
%% hz_format:approx_amount({$_, 4}, 12, 100500040123000004500000001) ->
|
||||||
%% "木123,456,789.123".
|
%% 木1_0050_0040.1230_0000_45...
|
||||||
%%
|
|
||||||
%% amount(gaju, {$,, 3}, 3, 123456789123456789123456789) ->
|
|
||||||
%% "木123,456,789.123...".
|
|
||||||
%%
|
|
||||||
%% amount(gaju, {$,, 3}, 6, 123456789123000000000000000) ->
|
|
||||||
%% "木123,456,789.123"
|
|
||||||
%%
|
|
||||||
%% amount(gaju, {$_, 4}, 10, 123456789123456789123456789) ->
|
|
||||||
%% "木1_2345_6789.1234_5678_91..."
|
|
||||||
%%
|
|
||||||
%% amount(gaju, jp, 3, 123456789123456789123456789) ->
|
|
||||||
%% "1億2345万6789木 12京3000兆本"
|
|
||||||
%%
|
|
||||||
%% amount(gaju, jp, 6, 123456789123456789123456789) ->
|
|
||||||
%% "1億2345万6789木 12京3456兆本"
|
|
||||||
%%
|
|
||||||
%% amount(gaju, jp, 0, 123456789123456789123456789) ->
|
|
||||||
%% "1億2345万6789木"
|
|
||||||
%%
|
|
||||||
%% amount(puck, jp, all, 123456789123456789123456789) ->
|
|
||||||
%% "123秭4567垓8912京3456兆7891億2345万6789本"
|
|
||||||
%% '''
|
%% '''
|
||||||
|
|
||||||
amount(gaju, us, Precision, Pucks) ->
|
approx_amount(us, Precision, Pucks) ->
|
||||||
western($,, $., 3, Precision, Pucks);
|
western($,, $., 3, Precision, Pucks);
|
||||||
amount(gaju, jp, Precision, Pucks) ->
|
approx_amount({Separator, Span}, Precision, Pucks) ->
|
||||||
jp(gaju, Precision, Pucks);
|
western(Separator, $., Span, Precision, Pucks).
|
||||||
amount(gaju, metric, Precision, Pucks) ->
|
|
||||||
bestern(gaju, ranks(metric), Precision, Pucks);
|
|
||||||
amount(gaju, legacy, Precision, Pucks) ->
|
|
||||||
bestern(gaju, ranks(heresy), Precision, Pucks);
|
|
||||||
amount(gaju, {Separator, Span}, Precision, Pucks) ->
|
|
||||||
western(Separator, $., Span, Precision, Pucks);
|
|
||||||
amount(puck, us, _, Pucks) ->
|
|
||||||
western($,, 3, Pucks);
|
|
||||||
amount(puck, jp, _, Pucks) ->
|
|
||||||
jp(puck, all, Pucks);
|
|
||||||
amount(puck, metric, _, Pucks) ->
|
|
||||||
bestern(puck, ranks(metric), all, Pucks);
|
|
||||||
amount(puck, legacy, _, Pucks) ->
|
|
||||||
bestern(puck, ranks(heresy), all, Pucks);
|
|
||||||
amount(puck, {Separator, Span}, _, Pucks) ->
|
|
||||||
western(Separator, Span, Pucks).
|
|
||||||
|
|
||||||
|
|
||||||
western(Separator, Span, Pucks) when Pucks >= 0 ->
|
western(Separator, Span, Pucks) when Pucks >= 0 ->
|
||||||
@@ -150,7 +194,7 @@ western(Separator, Span, Pucks) when Pucks < 0 ->
|
|||||||
|
|
||||||
western2(Separator, Span, Pucks) ->
|
western2(Separator, Span, Pucks) ->
|
||||||
P = lists:reverse(integer_to_list(Pucks)),
|
P = lists:reverse(integer_to_list(Pucks)),
|
||||||
[puck_mark() | separate(Separator, Span, P)].
|
[mark(puck) | separate(Separator, Span, P)].
|
||||||
|
|
||||||
|
|
||||||
western(Separator, Break, Span, Precision, Pucks) when Pucks >= 0 ->
|
western(Separator, Break, Span, Precision, Pucks) when Pucks >= 0 ->
|
||||||
@@ -160,30 +204,30 @@ western(Separator, Break, Span, Precision, Pucks) when Pucks < 0 ->
|
|||||||
|
|
||||||
|
|
||||||
western2(Separator, _, Span, 0, Pucks) ->
|
western2(Separator, _, Span, 0, Pucks) ->
|
||||||
G = lists:reverse(integer_to_list(Pucks div one_gaju())),
|
G = lists:reverse(integer_to_list(Pucks div one(gaju))),
|
||||||
[gaju_mark() | separate(Separator, Span, G)];
|
[mark(gaju) | separate(Separator, Span, G)];
|
||||||
western2(Separator, Break, Span, Precision, Pucks) ->
|
western2(Separator, Break, Span, Precision, Pucks) ->
|
||||||
SP = integer_to_list(Pucks),
|
SP = integer_to_list(Pucks),
|
||||||
Length = length(SP),
|
Length = length(SP),
|
||||||
Over18 = Length > 18,
|
Over18 = Length > 18,
|
||||||
NoPucks = (Pucks rem one_gaju()) =:= 0,
|
NoPucks = (Pucks rem one(gaju)) =:= 0,
|
||||||
case {Over18, NoPucks} of
|
case {Over18, NoPucks} of
|
||||||
{true, true} ->
|
{true, true} ->
|
||||||
Gs = lists:reverse(lists:sublist(SP, Length - 18)),
|
Gs = lists:reverse(lists:sublist(SP, Length - 18)),
|
||||||
[gaju_mark() | separate(Separator, Span, Gs)];
|
[mark(gaju) | separate(Separator, Span, Gs)];
|
||||||
{true, false} ->
|
{true, false} ->
|
||||||
{PChars, GChars} = lists:split(18, lists:reverse(SP)),
|
{PChars, GChars} = lists:split(18, lists:reverse(SP)),
|
||||||
H = [gaju_mark() | separate(Separator, Span, GChars)],
|
H = [mark(gaju) | separate(Separator, Span, GChars)],
|
||||||
{P, E} = decimal_pucks(Precision, lists:reverse(PChars)),
|
{P, E} = decimal_pucks(Precision, lists:reverse(PChars)),
|
||||||
T = lists:reverse(separate(Separator, Span, P)),
|
T = lists:reverse(separate(Separator, Span, P)),
|
||||||
lists:flatten([H, Break, T, E]);
|
lists:flatten([H, Break, T, E]);
|
||||||
{false, true} ->
|
{false, true} ->
|
||||||
[gaju_mark(), $0];
|
[mark(gaju), $0];
|
||||||
{false, false} ->
|
{false, false} ->
|
||||||
PChars = lists:flatten(string:pad(SP, 18, leading, $0)),
|
PChars = lists:flatten(string:pad(SP, 18, leading, $0)),
|
||||||
{P, E} = decimal_pucks(Precision, PChars),
|
{P, E} = decimal_pucks(Precision, PChars),
|
||||||
T = lists:reverse(separate(Separator, Span, P)),
|
T = lists:reverse(separate(Separator, Span, P)),
|
||||||
lists:flatten([gaju_mark(), $0, Break, T, E])
|
lists:flatten([mark(gaju), $0, Break, T, E])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
decimal_pucks(all, PChars) ->
|
decimal_pucks(all, PChars) ->
|
||||||
@@ -212,13 +256,13 @@ separate(S, P, N, [H | T], A) ->
|
|||||||
|
|
||||||
|
|
||||||
bestern(gaju, Ranks, Precision, Pucks) when Pucks >= 0 ->
|
bestern(gaju, Ranks, Precision, Pucks) when Pucks >= 0 ->
|
||||||
[gaju_mark(), bestern2(gaju, Ranks, 3, Precision, Pucks)];
|
[mark(gaju), bestern2(gaju, Ranks, 3, Precision, Pucks)];
|
||||||
bestern(gaju, Ranks, Precision, Pucks) when Pucks < 0 ->
|
bestern(gaju, Ranks, Precision, Pucks) when Pucks < 0 ->
|
||||||
[$-, gaju_mark(), bestern2(gaju, Ranks, 3, Precision, Pucks * -1)];
|
[$-, mark(gaju), bestern2(gaju, Ranks, 3, Precision, Pucks * -1)];
|
||||||
bestern(puck, Ranks, Precision, Pucks) when Pucks >= 0 ->
|
bestern(puck, Ranks, Precision, Pucks) when Pucks >= 0 ->
|
||||||
[puck_mark(), bestern2(puck, Ranks, 3, Precision, Pucks)];
|
[mark(puck), bestern2(puck, Ranks, 3, Precision, Pucks)];
|
||||||
bestern(puck, Ranks, Precision, Pucks) when Pucks < 0 ->
|
bestern(puck, Ranks, Precision, Pucks) when Pucks < 0 ->
|
||||||
[$-, puck_mark(), bestern2(puck, Ranks, 3, Precision, Pucks * -1)].
|
[$-, mark(puck), bestern2(puck, Ranks, 3, Precision, Pucks * -1)].
|
||||||
|
|
||||||
jp(Unit, Precision, Pucks) when Pucks >= 0 ->
|
jp(Unit, Precision, Pucks) when Pucks >= 0 ->
|
||||||
bestern2(Unit, ranks(jp), 4, Precision, Pucks);
|
bestern2(Unit, ranks(jp), 4, Precision, Pucks);
|
||||||
@@ -226,24 +270,24 @@ jp(Unit, Precision, Pucks) when Pucks < 0 ->
|
|||||||
[$-, bestern2(Unit, ranks(jp), 4, Precision, Pucks * -1)].
|
[$-, bestern2(Unit, ranks(jp), 4, Precision, Pucks * -1)].
|
||||||
|
|
||||||
bestern2(gaju, Ranks, Span, 0, Pucks) ->
|
bestern2(gaju, Ranks, Span, 0, Pucks) ->
|
||||||
G = lists:reverse(integer_to_list(Pucks div one_gaju())),
|
G = lists:reverse(integer_to_list(Pucks div one(gaju))),
|
||||||
case Span of
|
case Span of
|
||||||
3 -> period("G", Ranks, G);
|
3 -> period("G", Ranks, G);
|
||||||
4 -> myriad(gaju_mark(), Ranks, G)
|
4 -> myriad(mark(gaju), Ranks, G)
|
||||||
end;
|
end;
|
||||||
bestern2(gaju, Ranks, Span, all, Pucks) ->
|
bestern2(gaju, Ranks, Span, all, Pucks) ->
|
||||||
P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0)),
|
P = lists:flatten(string:pad(integer_to_list(Pucks rem one(gaju)), 18, leading, $0)),
|
||||||
Zilch = lists:all(fun(C) -> C =:= $0 end, P),
|
Zilch = lists:all(fun(C) -> C =:= $0 end, P),
|
||||||
{H, T} =
|
{H, T} =
|
||||||
case {Span, Zilch} of
|
case {Span, Zilch} of
|
||||||
{3, false} -> {bestern2(gaju, Ranks, 3, 0, Pucks), period("P", Ranks, lists:reverse(P))};
|
{3, false} -> {bestern2(gaju, Ranks, 3, 0, Pucks), period("P", Ranks, lists:reverse(P))};
|
||||||
{4, false} -> {jp(gaju, 0, Pucks), myriad(puck_mark(), Ranks, lists:reverse(P))};
|
{4, false} -> {jp(gaju, 0, Pucks), myriad(mark(puck), Ranks, lists:reverse(P))};
|
||||||
{3, true} -> {bestern2(gaju, Ranks, 3, 0, Pucks), ""};
|
{3, true} -> {bestern2(gaju, Ranks, 3, 0, Pucks), ""};
|
||||||
{4, true} -> {jp(gaju, 0, Pucks), ""}
|
{4, true} -> {jp(gaju, 0, Pucks), ""}
|
||||||
end,
|
end,
|
||||||
lists:flatten([H, " ", T]);
|
lists:flatten([H, " ", T]);
|
||||||
bestern2(gaju, Ranks, Span, Precision, Pucks) ->
|
bestern2(gaju, Ranks, Span, Precision, Pucks) ->
|
||||||
P = lists:flatten(string:pad(integer_to_list(Pucks rem one_gaju()), 18, leading, $0)),
|
P = lists:flatten(string:pad(integer_to_list(Pucks rem one(gaju)), 18, leading, $0)),
|
||||||
H =
|
H =
|
||||||
case Span of
|
case Span of
|
||||||
3 -> bestern2(gaju, Ranks, 3, 0, Pucks);
|
3 -> bestern2(gaju, Ranks, 3, 0, Pucks);
|
||||||
@@ -259,7 +303,7 @@ bestern2(gaju, Ranks, Span, Precision, Pucks) ->
|
|||||||
false ->
|
false ->
|
||||||
case Span of
|
case Span of
|
||||||
3 -> period("P", Ranks, PuckingString);
|
3 -> period("P", Ranks, PuckingString);
|
||||||
4 -> myriad(puck_mark(), Ranks, PuckingString)
|
4 -> myriad(mark(puck), Ranks, PuckingString)
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
""
|
""
|
||||||
@@ -274,12 +318,12 @@ bestern2(puck, Ranks, Span, all, Pucks) ->
|
|||||||
false ->
|
false ->
|
||||||
case Span of
|
case Span of
|
||||||
3 -> period("P", Ranks, P);
|
3 -> period("P", Ranks, P);
|
||||||
4 -> myriad(puck_mark(), Ranks, P)
|
4 -> myriad(mark(puck), Ranks, P)
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
case Span of
|
case Span of
|
||||||
3 -> [$0, " P"];
|
3 -> [$0, " P"];
|
||||||
4 -> [$0, puck_mark()]
|
4 -> [$0, mark(puck)]
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
bestern2(puck, Ranks, Span, Precision, Pucks) ->
|
bestern2(puck, Ranks, Span, Precision, Pucks) ->
|
||||||
@@ -289,7 +333,7 @@ bestern2(puck, Ranks, Span, Precision, Pucks) ->
|
|||||||
true ->
|
true ->
|
||||||
case Span of
|
case Span of
|
||||||
3 -> [$0, " P"];
|
3 -> [$0, " P"];
|
||||||
4 -> [$0, puck_mark()]
|
4 -> [$0, mark(puck)]
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
PucksToGive = lists:sublist(P, Digits),
|
PucksToGive = lists:sublist(P, Digits),
|
||||||
@@ -298,12 +342,12 @@ bestern2(puck, Ranks, Span, Precision, Pucks) ->
|
|||||||
false ->
|
false ->
|
||||||
case Span of
|
case Span of
|
||||||
3 -> period("P", Ranks, PuckingString);
|
3 -> period("P", Ranks, PuckingString);
|
||||||
4 -> myriad(puck_mark(), Ranks, PuckingString)
|
4 -> myriad(mark(puck), Ranks, PuckingString)
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
case Span of
|
case Span of
|
||||||
3 -> [$0, " P"];
|
3 -> [$0, " P"];
|
||||||
4 -> [$0, puck_mark()]
|
4 -> [$0, mark(puck)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
@@ -418,11 +462,11 @@ ranks(heresy) ->
|
|||||||
["k ", "m ", "b ", "t ", "q ", "e ", "z ", "y ", "r ", "Q "].
|
["k ", "m ", "b ", "t ", "q ", "e ", "z ", "y ", "r ", "Q "].
|
||||||
|
|
||||||
|
|
||||||
gaju_mark() -> $木.
|
mark(gaju) -> $木;
|
||||||
|
mark(puck) -> $本.
|
||||||
|
|
||||||
puck_mark() -> $本.
|
one(gaju) -> 1_000_000_000_000_000_000;
|
||||||
|
one(puck) -> 1.
|
||||||
one_gaju() -> 1_000_000_000_000_000_000.
|
|
||||||
|
|
||||||
|
|
||||||
-spec read(Format) -> Result
|
-spec read(Format) -> Result
|
||||||
@@ -430,10 +474,26 @@ one_gaju() -> 1_000_000_000_000_000_000.
|
|||||||
Result :: {ok, Pucks} | error,
|
Result :: {ok, Pucks} | error,
|
||||||
Pucks :: integer().
|
Pucks :: integer().
|
||||||
%% @doc
|
%% @doc
|
||||||
%% Convery any valid string formatted representation and output a value in pucks.
|
%% Convert any valid string formatted representation and output a value in pucks.
|
||||||
%% This routine can fail in the special case of `ch' style formatting with a single
|
%% NOTE: This function does not accept approximated values.
|
||||||
%% comma and/or a single period in it, as this can trigger misinterpretation as `us'
|
%% ```
|
||||||
%% style. When in doubt, always call `read/2' with a style specified.
|
%% 1> hz_format:read("木100,500,040.123,000,004,5").
|
||||||
|
%% {ok,100500040123000004500000000}
|
||||||
|
%% 2> hz_format:read("本100,500,040,123,000,004,500,000,000").
|
||||||
|
%% {ok,100500040123000004500000000}
|
||||||
|
%% 3> hz_format:read("1億50万40木 12京3000兆45億本").
|
||||||
|
%% {ok,100500040123000004500000000}
|
||||||
|
%% 4> hz_format:read("100秭5000垓4012京3000兆45億本").
|
||||||
|
%% {ok,100500040123000004500000000}
|
||||||
|
%% 5> hz_format:read("木100m 500k 40 G 123p 4g 500m P").
|
||||||
|
%% {ok,100500040123000004500000000}
|
||||||
|
%% 6> hz_format:read("本100y 500z 40e 123p 4g 500m P").
|
||||||
|
%% {ok,100500040123000004500000000}
|
||||||
|
%% 7> hz_format:read("木100m 500k 40 G 123q 4b 500m P").
|
||||||
|
%% {ok,100500040123000004500000000}
|
||||||
|
%% 8> hz_format:read("本100y 500z 40e 123q 4b 500m P").
|
||||||
|
%% {ok,100500040123000004500000000}
|
||||||
|
%% '''
|
||||||
|
|
||||||
read([$木 | Rest]) ->
|
read([$木 | Rest]) ->
|
||||||
read_w_gajus(Rest, []);
|
read_w_gajus(Rest, []);
|
||||||
@@ -459,6 +519,8 @@ read([C | Rest]) when $0 =< C andalso C =< $9 ->
|
|||||||
read([C | Rest]) when $0 =< C andalso C =< $9 ->
|
read([C | Rest]) when $0 =< C andalso C =< $9 ->
|
||||||
NumC = C - $0 + $0,
|
NumC = C - $0 + $0,
|
||||||
read(Rest, [NumC], []);
|
read(Rest, [NumC], []);
|
||||||
|
read(String) when is_binary(String) ->
|
||||||
|
read(binary_to_list(String));
|
||||||
read(_) ->
|
read(_) ->
|
||||||
error.
|
error.
|
||||||
|
|
||||||
@@ -474,13 +536,13 @@ read_w_gajus([$_ | Rest], A) ->
|
|||||||
read_w_gajus([$. | Rest], A) ->
|
read_w_gajus([$. | Rest], A) ->
|
||||||
case read_w_pucks(Rest, []) of
|
case read_w_pucks(Rest, []) of
|
||||||
{ok, P} ->
|
{ok, P} ->
|
||||||
G = list_to_integer(lists:reverse(A)) * one_gaju(),
|
G = list_to_integer(lists:reverse(A)) * one(gaju),
|
||||||
{ok, G + P};
|
{ok, G + P};
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
end;
|
end;
|
||||||
read_w_gajus([], A) ->
|
read_w_gajus([], A) ->
|
||||||
G = list_to_integer(lists:reverse(A)) * one_gaju(),
|
G = list_to_integer(lists:reverse(A)) * one(gaju),
|
||||||
{ok, G};
|
{ok, G};
|
||||||
read_w_gajus([C, 32 | Rest], A) ->
|
read_w_gajus([C, 32 | Rest], A) ->
|
||||||
read(Rest, [], [{C, A}]);
|
read(Rest, [], [{C, A}]);
|
||||||
@@ -488,6 +550,8 @@ read_w_gajus([32, $G, 32 | Rest], A) ->
|
|||||||
read(Rest, [], [{$G, A}], []);
|
read(Rest, [], [{$G, A}], []);
|
||||||
read_w_gajus([32, $G], A) ->
|
read_w_gajus([32, $G], A) ->
|
||||||
calc([{$G, A}], []);
|
calc([{$G, A}], []);
|
||||||
|
read_w_gajus([32, $P], A) ->
|
||||||
|
calc([], [{$P, A}]);
|
||||||
read_w_gajus(_, _) ->
|
read_w_gajus(_, _) ->
|
||||||
error.
|
error.
|
||||||
|
|
||||||
@@ -500,6 +564,10 @@ read_w_pucks([$, | Rest], A) ->
|
|||||||
read_w_pucks(Rest, A);
|
read_w_pucks(Rest, A);
|
||||||
read_w_pucks([$_ | Rest], A) ->
|
read_w_pucks([$_ | Rest], A) ->
|
||||||
read_w_pucks(Rest, A);
|
read_w_pucks(Rest, A);
|
||||||
|
read_w_pucks([C, 32 | Rest], A) ->
|
||||||
|
read(Rest, [], [], [{C, A}]);
|
||||||
|
read_w_pucks([32, $P], A) ->
|
||||||
|
calc([], [{$P, A}]);
|
||||||
read_w_pucks([], A) ->
|
read_w_pucks([], A) ->
|
||||||
Padded = lists:flatten(string:pad(lists:reverse(A), 18, trailing, $0)),
|
Padded = lists:flatten(string:pad(lists:reverse(A), 18, trailing, $0)),
|
||||||
{ok, list_to_integer(Padded)}.
|
{ok, list_to_integer(Padded)}.
|
||||||
@@ -514,6 +582,10 @@ read([$木], A, G) ->
|
|||||||
calc([{$G, A} | G], []);
|
calc([{$G, A} | G], []);
|
||||||
read([$G], A, G) ->
|
read([$G], A, G) ->
|
||||||
calc([{$G, A} | G], []);
|
calc([{$G, A} | G], []);
|
||||||
|
read([32, $G], A, G) ->
|
||||||
|
calc([{$G, A} | G], []);
|
||||||
|
read([32, $G, 32 | Rest], A, G) ->
|
||||||
|
read(Rest, [], [{$G, A} | G], []);
|
||||||
read([$木, 32 | Rest], A, G) ->
|
read([$木, 32 | Rest], A, G) ->
|
||||||
read(Rest, [], [{$G, A} | G], []);
|
read(Rest, [], [{$G, A} | G], []);
|
||||||
read([$G, 32 | Rest], A, G) ->
|
read([$G, 32 | Rest], A, G) ->
|
||||||
@@ -522,12 +594,27 @@ read([$本], A, P) ->
|
|||||||
calc([], [{$P, A} | P]);
|
calc([], [{$P, A} | P]);
|
||||||
read([$P], A, P) ->
|
read([$P], A, P) ->
|
||||||
calc([], [{$P, A} | P]);
|
calc([], [{$P, A} | P]);
|
||||||
|
read([32, $P], A, P) ->
|
||||||
|
calc([], [{$P, A} | P]);
|
||||||
read([C, 32 | Rest], A, G) ->
|
read([C, 32 | Rest], A, G) ->
|
||||||
read(Rest, [], [{C, A} | G]);
|
read(Rest, [], [{C, A} | G]);
|
||||||
|
read([$, | Rest], A, []) ->
|
||||||
|
read_w_gajus(Rest, A);
|
||||||
|
read([$_ | Rest], A, []) ->
|
||||||
|
read_w_gajus(Rest, A);
|
||||||
|
read([$. | Rest], A, []) ->
|
||||||
|
case read_w_pucks(Rest, []) of
|
||||||
|
{ok, P} ->
|
||||||
|
G = list_to_integer(lists:reverse(A)) * one(gaju),
|
||||||
|
{ok, G + P};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end;
|
||||||
read([C | Rest], A, G) ->
|
read([C | Rest], A, G) ->
|
||||||
read(Rest, [], [{C, A} | G]);
|
read(Rest, [], [{C, A} | G]);
|
||||||
read(Rest, A, G) ->
|
read([], A, []) ->
|
||||||
io:format("read(\"~ts\", ~tp, ~tp) -> died!~n", [Rest, A, G]),
|
read_w_gajus([], A);
|
||||||
|
read(_, _, _) ->
|
||||||
error.
|
error.
|
||||||
|
|
||||||
|
|
||||||
@@ -540,12 +627,13 @@ read([$本], A, G, P) ->
|
|||||||
calc(G, [{$P, A} | P]);
|
calc(G, [{$P, A} | P]);
|
||||||
read([$P], A, G, P) ->
|
read([$P], A, G, P) ->
|
||||||
calc(G, [{$P, A} | P]);
|
calc(G, [{$P, A} | P]);
|
||||||
|
read([32, $P], A, G, P) ->
|
||||||
|
calc(G, [{$P, A} | P]);
|
||||||
read([C, 32 | Rest], A, G, P) ->
|
read([C, 32 | Rest], A, G, P) ->
|
||||||
read(Rest, [], G, [{C, A} | P]);
|
read(Rest, [], G, [{C, A} | P]);
|
||||||
read([C | Rest], A, G, P) ->
|
read([C | Rest], A, G, P) ->
|
||||||
read(Rest, [], G, [{C, A} | P]);
|
read(Rest, [], G, [{C, A} | P]);
|
||||||
read(_, _, _, _) ->
|
read(_, _, _, _) ->
|
||||||
io:format("read/4 died!~n"),
|
|
||||||
error.
|
error.
|
||||||
|
|
||||||
calc(G, P) ->
|
calc(G, P) ->
|
||||||
@@ -564,7 +652,7 @@ calc(U, [{_, []} | S], A) ->
|
|||||||
calc(U, [{M, Cs} | S], A) ->
|
calc(U, [{M, Cs} | S], A) ->
|
||||||
case magnitude(M) of
|
case magnitude(M) of
|
||||||
{ok, J} ->
|
{ok, J} ->
|
||||||
N = list_to_integer(lists:reverse(Cs)) * J * unit(U),
|
N = list_to_integer(lists:reverse(Cs)) * J * one(U),
|
||||||
calc(U, S, A + N);
|
calc(U, S, A + N);
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
@@ -572,9 +660,6 @@ calc(U, [{M, Cs} | S], A) ->
|
|||||||
calc(_, [], A) ->
|
calc(_, [], A) ->
|
||||||
{ok, A}.
|
{ok, A}.
|
||||||
|
|
||||||
unit(gaju) -> one_gaju();
|
|
||||||
unit(puck) -> 1.
|
|
||||||
|
|
||||||
|
|
||||||
magnitude($G) ->
|
magnitude($G) ->
|
||||||
{ok, 1};
|
{ok, 1};
|
||||||
@@ -607,7 +692,7 @@ rank(_, [], _, _) ->
|
|||||||
%% in Gajus. Useful for formatting generic output for UI elements
|
%% in Gajus. Useful for formatting generic output for UI elements
|
||||||
|
|
||||||
price_to_string(Pucks) ->
|
price_to_string(Pucks) ->
|
||||||
Gaju = 1_000_000_000_000_000_000,
|
Gaju = one(gaju),
|
||||||
H = integer_to_list(Pucks div Gaju),
|
H = integer_to_list(Pucks div Gaju),
|
||||||
R = Pucks rem Gaju,
|
R = Pucks rem Gaju,
|
||||||
case string:strip(lists:flatten(io_lib:format("~18..0w", [R])), right, $0) of
|
case string:strip(lists:flatten(io_lib:format("~18..0w", [R])), right, $0) of
|
||||||
|
|||||||
+71
-10
@@ -37,15 +37,15 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_grids).
|
-module(hz_grids).
|
||||||
-vsn("0.7.0").
|
-vsn("0.9.1").
|
||||||
-export([url/2, parse/1, req/2, req/3]).
|
-export([url/2, url/3, url/4, parse/1, req/2, req/3, req/4]).
|
||||||
|
|
||||||
|
|
||||||
-spec url(Instruction, HTTP) -> Result
|
-spec url(Instruction, HTTP) -> Result
|
||||||
when Instruction :: spend | transfer | sign,
|
when Instruction :: spend | transfer | sign,
|
||||||
HTTP :: uri_string:uri_string(),
|
HTTP :: uri_string:uri_string(),
|
||||||
GRIDS :: uri_string:uri_string(),
|
Result :: {ok, GRIDS} | uri_string:uri_error(),
|
||||||
Result :: {ok, GRIDS} | uri_string:uri_error().
|
GRIDS :: uri_string:uri_string().
|
||||||
%% @doc
|
%% @doc
|
||||||
%% Takes
|
%% Takes
|
||||||
|
|
||||||
@@ -66,6 +66,63 @@ url2(Instruction, URL = #{path := Path}) ->
|
|||||||
{ok, uri_string:recompose(GRIDS)}.
|
{ok, uri_string:recompose(GRIDS)}.
|
||||||
|
|
||||||
|
|
||||||
|
-spec url(Instruction, Recipient, Amount) -> GRIDS
|
||||||
|
when Instruction :: {spend, Network} | {transfer, Node},
|
||||||
|
Network :: string(),
|
||||||
|
Node :: {inet:ip_address() | inet:hostname(), inet:port_number()}
|
||||||
|
| uri_string:uri_string(),
|
||||||
|
Recipient :: string(),
|
||||||
|
Amount :: non_neg_integer(),
|
||||||
|
GRIDS :: uri_string:uri_string().
|
||||||
|
%% @doc
|
||||||
|
%% Forms a GRIDS URL for spends or transfers.
|
||||||
|
%% @equiv uri(Instruction, Recipient, Amount, "")
|
||||||
|
|
||||||
|
url(Instruction, Recipient, Amount) ->
|
||||||
|
url(Instruction, Recipient, Amount, "").
|
||||||
|
|
||||||
|
|
||||||
|
-spec url(Instruction, Recipient, Amount, Payload) -> GRIDS
|
||||||
|
when Instruction :: {spend, Network} | {transfer, Node},
|
||||||
|
Network :: string(),
|
||||||
|
Node :: {inet:ip_address() | inet:hostname(), inet:port_number()}
|
||||||
|
| uri_string:uri_string(), % "http://..." | "https://..."
|
||||||
|
Recipient :: string(),
|
||||||
|
Amount :: non_neg_integer() | none,
|
||||||
|
Payload :: binary(),
|
||||||
|
GRIDS :: uri_string:uri_string().
|
||||||
|
%% @doc
|
||||||
|
%% Forms a GRIDS URL for spends or transfers.
|
||||||
|
|
||||||
|
url({spend, Network}, Recipient, Amount, Payload) ->
|
||||||
|
Elements = ["grids://", Network, "/1/s/", Recipient, qwargs(Amount, Payload)],
|
||||||
|
unicode:characters_to_list(Elements);
|
||||||
|
url({transfer, Node}, Recipient, Amount, Payload) ->
|
||||||
|
Prefix =
|
||||||
|
case Node of
|
||||||
|
{H, P} -> ["grid://", h_to_s(H), ":", integer_to_list(P)];
|
||||||
|
"https://" ++ H -> ["grids://", H];
|
||||||
|
"http://" ++ H -> ["grid://", H];
|
||||||
|
<<"https://", H/binary>> -> ["grids://", H];
|
||||||
|
<<"http://", H/binary>> -> ["grid://", H]
|
||||||
|
end,
|
||||||
|
unicode:characters_to_list([Prefix, "/1/t/", Recipient, qwargs(Amount, Payload)]).
|
||||||
|
|
||||||
|
h_to_s(Host) when is_list(Host) -> Host;
|
||||||
|
h_to_s(Host) when is_binary(Host) -> Host;
|
||||||
|
h_to_s(Host) when is_tuple(Host) -> inet:ntoa(Host);
|
||||||
|
h_to_s(Host) when is_atom(Host) -> atom_to_list(Host).
|
||||||
|
|
||||||
|
qwargs(none, "") ->
|
||||||
|
[];
|
||||||
|
qwargs(Amount, "") ->
|
||||||
|
["?a=", integer_to_list(Amount)];
|
||||||
|
qwargs(none, Payload) ->
|
||||||
|
[$? | uri_string:compose_query([{"p", Payload}])];
|
||||||
|
qwargs(Amount, Payload) ->
|
||||||
|
[$? | uri_string:compose_query([{"a", integer_to_list(Amount)}, {"p", Payload}])].
|
||||||
|
|
||||||
|
|
||||||
-spec parse(GRIDS) -> Result
|
-spec parse(GRIDS) -> Result
|
||||||
when GRIDS :: string(),
|
when GRIDS :: string(),
|
||||||
Result :: {ok, Instruction} | uri_string:error(),
|
Result :: {ok, Instruction} | uri_string:error(),
|
||||||
@@ -136,24 +193,28 @@ l_to_i(S) ->
|
|||||||
req(Type, Message) ->
|
req(Type, Message) ->
|
||||||
req(Type, Message, false).
|
req(Type, Message, false).
|
||||||
|
|
||||||
req(sign, Message, ID) ->
|
req(Type, Message, ID) ->
|
||||||
|
{ok, NetworkID} = hz:network_id(),
|
||||||
|
req(Type, Message, ID, NetworkID).
|
||||||
|
|
||||||
|
req(sign, Message, ID, NetworkID) ->
|
||||||
#{"grids" => 1,
|
#{"grids" => 1,
|
||||||
"chain" => "gajumaru",
|
"chain" => "gajumaru",
|
||||||
"network_id" => hz:network_id(),
|
"network_id" => NetworkID,
|
||||||
"type" => "message",
|
"type" => "message",
|
||||||
"public_id" => ID,
|
"public_id" => ID,
|
||||||
"payload" => Message};
|
"payload" => Message};
|
||||||
req(tx, Data, ID) ->
|
req(tx, Data, ID, NetworkID) ->
|
||||||
#{"grids" => 1,
|
#{"grids" => 1,
|
||||||
"chain" => "gajumaru",
|
"chain" => "gajumaru",
|
||||||
"network_id" => hz:network_id(),
|
"network_id" => NetworkID,
|
||||||
"type" => "tx",
|
"type" => "tx",
|
||||||
"public_id" => ID,
|
"public_id" => ID,
|
||||||
"payload" => Data};
|
"payload" => Data};
|
||||||
req(ack, Message, ID) ->
|
req(ack, Message, ID, NetworkID) ->
|
||||||
#{"grids" => 1,
|
#{"grids" => 1,
|
||||||
"chain" => "gajumaru",
|
"chain" => "gajumaru",
|
||||||
"network_id" => hz:network_id(),
|
"network_id" => NetworkID,
|
||||||
"type" => "ack",
|
"type" => "ack",
|
||||||
"public_id" => ID,
|
"public_id" => ID,
|
||||||
"payload" => Message}.
|
"payload" => Message}.
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_key_master).
|
-module(hz_key_master).
|
||||||
-vsn("0.7.0").
|
-vsn("0.9.1").
|
||||||
|
|
||||||
|
|
||||||
-export([make_key/1, encode/1, decode/1]).
|
-export([make_key/1, encode/1, decode/1]).
|
||||||
-export([lcg/1]).
|
-export([lcg/1]).
|
||||||
|
|||||||
+37
-2
@@ -9,7 +9,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_man).
|
-module(hz_man).
|
||||||
-vsn("0.7.0").
|
-vsn("0.9.1").
|
||||||
-behavior(gen_server).
|
-behavior(gen_server).
|
||||||
-author("Craig Everett <ceverett@tsuriai.jp>").
|
-author("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
-copyright("Craig Everett <ceverett@tsuriai.jp>").
|
||||||
@@ -20,6 +20,9 @@
|
|||||||
chain_nodes/0, chain_nodes/1,
|
chain_nodes/0, chain_nodes/1,
|
||||||
timeout/0, timeout/1]).
|
timeout/0, timeout/1]).
|
||||||
|
|
||||||
|
%% Contract caching
|
||||||
|
-export([cache_aaci/2, lookup_aaci/1]).
|
||||||
|
|
||||||
%% The whole point of this module:
|
%% The whole point of this module:
|
||||||
-export([request_sticky/1, request_sticky/2, request/1, request/2]).
|
-export([request_sticky/1, request_sticky/2, request/1, request/2]).
|
||||||
|
|
||||||
@@ -44,7 +47,8 @@
|
|||||||
chain_nodes = {[], []} :: {[hz:chain_node()], [hz:chain_node()]},
|
chain_nodes = {[], []} :: {[hz:chain_node()], [hz:chain_node()]},
|
||||||
sticky = none :: none | hz:chain_node(),
|
sticky = none :: none | hz:chain_node(),
|
||||||
fetchers = [] :: [#fetcher{}],
|
fetchers = [] :: [#fetcher{}],
|
||||||
timeout = 5000 :: pos_integer()}).
|
timeout = 5000 :: pos_integer(),
|
||||||
|
cache = #{} :: #{Label :: term() := AACI :: hz:aaci()}}).
|
||||||
|
|
||||||
|
|
||||||
-type state() :: #s{}.
|
-type state() :: #s{}.
|
||||||
@@ -94,6 +98,22 @@ timeout(Value) when 0 < Value, Value =< 120000 ->
|
|||||||
gen_server:cast(?MODULE, {timeout, Value}).
|
gen_server:cast(?MODULE, {timeout, Value}).
|
||||||
|
|
||||||
|
|
||||||
|
-spec cache_aaci(Label, AACI) -> ok
|
||||||
|
when Label :: term(),
|
||||||
|
AACI :: hz:aaci().
|
||||||
|
|
||||||
|
cache_aaci(Label, AACI) ->
|
||||||
|
gen_server:call(?MODULE, {cache, Label, AACI}).
|
||||||
|
|
||||||
|
|
||||||
|
-spec lookup_aaci(Label) -> Result
|
||||||
|
when Label :: term(),
|
||||||
|
Result :: {ok, hz:aaci()} | error.
|
||||||
|
|
||||||
|
lookup_aaci(Label) ->
|
||||||
|
gen_server:call(?MODULE, {lookup, Label}).
|
||||||
|
|
||||||
|
|
||||||
-spec request_sticky(Path) -> {ok, Value} | {error, Reason}
|
-spec request_sticky(Path) -> {ok, Value} | {error, Reason}
|
||||||
when Path :: unicode:charlist(),
|
when Path :: unicode:charlist(),
|
||||||
Value :: map(),
|
Value :: map(),
|
||||||
@@ -167,6 +187,12 @@ handle_call({request, Request}, From, State) ->
|
|||||||
handle_call({request_sticky, Request}, From, State) ->
|
handle_call({request_sticky, Request}, From, State) ->
|
||||||
NewState = do_request_sticky(Request, From, State),
|
NewState = do_request_sticky(Request, From, State),
|
||||||
{noreply, NewState};
|
{noreply, NewState};
|
||||||
|
handle_call({lookup, Label}, _, State) ->
|
||||||
|
Result = do_lookup(Label, State),
|
||||||
|
{reply, Result, State};
|
||||||
|
handle_call({cache, Label, AACI}, _, State) ->
|
||||||
|
NewState = do_cache_aaci(Label, AACI, State),
|
||||||
|
{reply, ok, NewState};
|
||||||
handle_call(tls, _, State = #s{tls = TLS}) ->
|
handle_call(tls, _, State = #s{tls = TLS}) ->
|
||||||
{reply, TLS, State};
|
{reply, TLS, State};
|
||||||
handle_call(chain_nodes, _, State) ->
|
handle_call(chain_nodes, _, State) ->
|
||||||
@@ -265,6 +291,15 @@ do_tls(_, State) ->
|
|||||||
State.
|
State.
|
||||||
|
|
||||||
|
|
||||||
|
do_cache_aaci(Label, AACI, State = #s{cache = Cache}) ->
|
||||||
|
NewCache = maps:put(Label, AACI, Cache),
|
||||||
|
State#s{cache = NewCache}.
|
||||||
|
|
||||||
|
|
||||||
|
do_lookup(Label, #s{cache = Cache}) ->
|
||||||
|
maps:find(Label, Cache).
|
||||||
|
|
||||||
|
|
||||||
do_request_sticky(_, From, State = #s{sticky = none}) ->
|
do_request_sticky(_, From, State = #s{sticky = none}) ->
|
||||||
ok = gen_server:reply(From, {error, no_nodes}),
|
ok = gen_server:reply(From, {error, no_nodes}),
|
||||||
State;
|
State;
|
||||||
|
|||||||
+1459
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
%%% @end
|
%%% @end
|
||||||
|
|
||||||
-module(hz_sup).
|
-module(hz_sup).
|
||||||
-vsn("0.7.0").
|
-vsn("0.9.1").
|
||||||
-behaviour(supervisor).
|
-behaviour(supervisor).
|
||||||
-author("Craig Everett <zxq9@zxq9.com>").
|
-author("Craig Everett <zxq9@zxq9.com>").
|
||||||
-copyright("Craig Everett <zxq9@zxq9.com>").
|
-copyright("Craig Everett <zxq9@zxq9.com>").
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{name,"Hakuzaru"}.
|
{name,"Hakuzaru"}.
|
||||||
{type,app}.
|
{type,app}.
|
||||||
{modules,[]}.
|
{modules,[]}.
|
||||||
|
{author,"Craig Everett"}.
|
||||||
{prefix,"hz"}.
|
{prefix,"hz"}.
|
||||||
{desc,"Gajumaru interoperation library"}.
|
{desc,"Gajumaru interoperation library"}.
|
||||||
{author,"Craig Everett"}.
|
{package_id,{"otpr","hakuzaru",{0,9,1}}}.
|
||||||
{package_id,{"otpr","hakuzaru",{0,7,0}}}.
|
|
||||||
{deps,[{"otpr","sophia",{9,0,0}},
|
{deps,[{"otpr","sophia",{9,0,0}},
|
||||||
{"otpr","gmserialization",{0,1,3}},
|
{"otpr","gmserialization",{0,1,3}},
|
||||||
{"otpr","gmbytecode",{3,4,1}},
|
{"otpr","gmbytecode",{3,4,1}},
|
||||||
@@ -19,6 +19,6 @@
|
|||||||
{copyright,"Craig Everett"}.
|
{copyright,"Craig Everett"}.
|
||||||
{file_exts,[]}.
|
{file_exts,[]}.
|
||||||
{license,"MIT"}.
|
{license,"MIT"}.
|
||||||
{repo_url,"https://gitlab.com/ioecs/hakuzaru"}.
|
{repo_url,"https://git.qpq.swiss/QPQ-AG/hakuzaru"}.
|
||||||
{tags,["qpq","gajumaru","blockchain","hakuzaru","crypto","defi"]}.
|
{tags,["qpq","gajumaru","blockchain","hakuzaru","crypto","defi"]}.
|
||||||
{ws_url,"https://gitlab.com/ioecs/hakuzaru"}.
|
{ws_url,"https://git.qpq.swiss/QPQ-AG/hakuzaru"}.
|
||||||
|
|||||||
Reference in New Issue
Block a user