Add some pages
parent
008b4b6963
commit
c162caabc4
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.swp
|
||||
*.swo
|
148
API-Encoding.md
Normal file
148
API-Encoding.md
Normal file
@ -0,0 +1,148 @@
|
||||
# API Encoding (`xy_ABCD` strings)
|
||||
|
||||
When you are interacting with Gajumaru you often encounter garbage strings like
|
||||
|
||||
```
|
||||
cb_OwQELwGfAKAgjs50MOABi6flmiNru6qg/5U9bjKymkvlywP7RtPnWASG88h7
|
||||
th_2H8EreT7LNw43jEG9yL7yvSw3AHbG2TTimMgVTYVFwqh6ucSeV
|
||||
ak_2CNR6NcNj5cFUa28wmVNyptiadtqcsqhG8qoQpPwULfyLiFHD6
|
||||
```
|
||||
|
||||
These are called "API strings" or "API encoding" in official lingo. Suppose you
|
||||
have a string `xy_ABCD`
|
||||
|
||||
- The `xy` prefix (`cb`, `th`, etc) indicates what sort of data is contained
|
||||
in the rest of the string, and whether it is Base64 or Base58. (See [[
|
||||
BaseN ]]).
|
||||
|
||||
Generally, anything that is both fixed-length and likely to be input
|
||||
manually (e.g. public keys) is going to be Base58, else it will be Base64.
|
||||
|
||||
- The `ABCD` part is binary data (plus some check bytes) that is encoded
|
||||
either in Base64 or Base58.
|
||||
|
||||
```erlang
|
||||
add_check_bytes(Bin) when is_binary(Bin) ->
|
||||
<<CheckBytes:4/bytes, _/binary>> = crypto:hash(sha256, crypto:hash(sha256, Bin)),
|
||||
<<Bin/binary, CheckBytes/binary>>.
|
||||
```
|
||||
|
||||
When you decode the `ABCD` stuff, it decodes to the binary data suffixed by
|
||||
the 4 check bytes (i.e. the **output** of the `add_check_bytes/1` function
|
||||
above).
|
||||
|
||||
- Sometimes the binary data is plain data (e.g. account public keys).
|
||||
Sometimes it's compound data (e.g. a transaction).
|
||||
|
||||
|
||||
## Example: Decoding Public Keys
|
||||
|
||||
For instance, account public keys are encoded in Base58 and have the prefix
|
||||
`ak_`
|
||||
|
||||
To decode one, get out the actual bytes of the public key, and check the
|
||||
check bytes, I wrote this:
|
||||
|
||||
```erlang
|
||||
do(["akd", AkStr]) ->
|
||||
"ak_" ++ Base58Shit = AkStr,
|
||||
CheckedBytes = gw_b58:dec(Base58Shit),
|
||||
ShaSha4 =
|
||||
fun(Bytes) ->
|
||||
<<CheckBytes:4/bytes, _/binary>> = crypto:hash(sha256, crypto:hash(sha256, Bytes)),
|
||||
CheckBytes
|
||||
end,
|
||||
<<DataBytes:(byte_size(CheckedBytes) - 4)/bytes,
|
||||
CheckBytes:4/bytes>> = CheckedBytes,
|
||||
io:format("~p~n", [DataBytes]),
|
||||
case ShaSha4(DataBytes) =:= CheckBytes of
|
||||
true -> io:format("checksum: passed~n", []);
|
||||
false -> io:format("checksum: failed~n", [])
|
||||
end;
|
||||
```
|
||||
|
||||
```
|
||||
[~] % gw akd ak_A3aMregStEULMXyPzNXWEfq1u75yM7BaQ5k8qVhCCpcvCr9Rx
|
||||
<<20,137,82,130,217,195,19,25,115,137,60,225,221,88,168,194,156,8,88,244,17,30,
|
||||
121,7,114,180,61,27,194,44,94,166>>
|
||||
checksum: passed
|
||||
```
|
||||
|
||||
## Compound Data (e.g. transactions)
|
||||
|
||||
If it's compound data (i.e. it has fields), then the binary data you get
|
||||
out of the decode process is going to be encoded in RLP. (See
|
||||
[[ RLP ]])
|
||||
|
||||
RLP-decode is going to give you a list-of-lists-of-\dots-of-binaries.
|
||||
|
||||
RLP data you get from Gajumaru is going to be of the format
|
||||
|
||||
```erlang
|
||||
[Tag, Version | Fields]
|
||||
```
|
||||
|
||||
The `Tag` and `Version` are going to be binaries that you need to pretend are
|
||||
integers.
|
||||
|
||||
The tag and version tell you what the format of the fields are going to be.
|
||||
|
||||
These formats are documented in [[ Serialization ]].
|
||||
|
||||
## The Representation Problem
|
||||
|
||||
This problem of what is conceptually one piece of data having 4 different
|
||||
representations that you have to juggle between in your code, I call **The
|
||||
Representation Problem**. This is the hardest practical problem to deal with as
|
||||
a developer using Gajumaru.
|
||||
|
||||
There is no real consistency or convention about when you use one
|
||||
representation over another.
|
||||
|
||||
For instance, the public key I showed you above. When you encode it as an
|
||||
`ak_...` string, you use just the 32-byte public key.
|
||||
|
||||
But when you are encoding that same key as a field to use in a spend
|
||||
transaction, you need to encode it as `<<1:8, Pubkey/binary>>`.
|
||||
|
||||
Some functions you call from Erlang code take the public key as an argument.
|
||||
There is no consistency about whether they want the `ak_...` string or if they
|
||||
want the 32 bytes, etc.
|
||||
|
||||
Sometimes the function you call wants some weird data structure. Like the `1:8`
|
||||
thing above corresponds to a convention about what sort of object the public
|
||||
key points to (because you can spend to a normal account, to a contract, to a
|
||||
name, etc; the number in the first byte indicates which type this is). There
|
||||
was some function I called the other day which needed that information, but
|
||||
instead of sending it the 33-byte augmented public key, it wanted a tuple that
|
||||
was something like `{{type, account_pubkey}, {key, Bytes}}`.
|
||||
|
||||
There is no consistency across different modules about which Erlang atoms are
|
||||
used. Sometimes it's `account_pubkey`, sometimes it's `account`. It's hell.
|
||||
In practice you have to just look at the source code of the function you're
|
||||
calling and see what it expects.
|
||||
|
||||
You also can't expect error messages that tell you what mistake you made
|
||||
and where, particularly when you're interacting with a node over HTTP
|
||||
(generally, HTTP wants API-encoded versions of things).
|
||||
|
||||
The Representation Problem is a (moderate) annoyance to excellent programmers.
|
||||
The worst thing it can do is nerdsnipe you and foolishly make you think the
|
||||
problem has some elegant solution and then you spend months trying to solve it
|
||||
before you realize that there is no solution because the problem itself is
|
||||
wrong.
|
||||
|
||||
This problem absolutely cripples average programmers trying to use Gajumaru.
|
||||
It's an open question about whether or not that's a good thing (average
|
||||
programmers probably shouldn't be anywhere near code that handles people's
|
||||
money), but also a question that is out of scope.
|
||||
|
||||
There are many layers to the onion. Also there are multiple differnt onion
|
||||
schema that sometimes collapse down to the same inner onion and sometimes you
|
||||
have to traverse down one onion and up another and also the onion hates you and
|
||||
it's rotten and poisonous.
|
||||
|
||||
All I can really do here is give you a field guide to how to deal with this
|
||||
problem in practice. The best practical solution in general is to quit trying
|
||||
to make sense of computers and give up on this whole programming thing and
|
||||
become a beekeeper instead.
|
0
BaseN.md
Normal file
0
BaseN.md
Normal file
7
Home.md
7
Home.md
@ -1,3 +1,8 @@
|
||||
# Gajumaru Wiki: Home
|
||||
|
||||
## Quick Reference
|
||||
## Quick Reference
|
||||
|
||||
1. [[ API Encoding ]] (`ak_...` garbage)
|
||||
2. [[ BaseN ]]
|
||||
3. [[ RLP ]]
|
||||
4. [[ Serializations ]]
|
||||
|
0
RLP.md
Normal file
0
RLP.md
Normal file
0
Serialization.md
Normal file
0
Serialization.md
Normal file
Loading…
x
Reference in New Issue
Block a user