This is a test repo to experiment with a standard for generating armored slates for Grin transactions.
It should be able to:
-
Serialize armored strings from
grin-wallet
prepared slate json strings and file bytes -
Deserialize armor prepared by
slatepack
as bytes or json string for use ingrin-wallet
- Create a new rust project with
cargo new grin-armor
andcd grin-armor
- In
Cargo.toml
under[Dependencies]
addslatepack = { git = "https://github.com/j01tz/slatepack" }
- In
/src/main.rs
add the following lines:
use slatepack;
...
let mut file = File::open("src/test.tx.bin").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let mut slate = slatepack::SerializedSlate::new();
slate.is_bin = true;
slate.bin = buffer;
let armor = slatepack::SerializedSlate::armor(&slate).unwrap();
println!("{}", armor.data);
- Create a legacy slate file:
grin-wallet --floonet send -d "/path/to/grin-armor/test.tx" -m binfile 1.337
- Next execute
cargo run
and the armor string will be printed to your terminal window - Share the armored string with your counterparty and have them read the next section
- When they have returned the response string you can save a
test.armor.response
file and access with code like above or directly as a pasted string:
use std::fs::File;
use std::io::Write;
...
let armor_response = "BEGINSLATEPACK...ENDSLATEPACK.".to_string();
let armor_format = slatepack::ArmoredSlate::new(&armor_response, true);
let response_slate = slatepack::ArmoredSlate::remove_armor(armor_format).unwrap();
let mut response_file = File::create("test.tx.response").unwrap();
response_file.write_all(&response_slate.bin).unwrap();
- Now finalize and broadcast the tx file with
grin-wallet --floonet finalize -i test.tx.response
- Start a new rust project with slatepack dependency as in steps in previous section
- Use some code like below to handle the armored string you receive:
use slatepack;
use std::fs::File;
use std::io::Write;
...
let armor = "BEGINSLATEPACK...ENDSLATEPACK.".to_string();
let armored_slate = slatepack::ArmoredSlate::new(&armor, true);
let slate = slatepack::ArmoredSlate::remove_armor(&armored_slate).unwrap();
let mut slate_file = File::create("test.tx").unwrap();
slate_file.write_all(&slate.bin).unwrap();
- Next execute
cargo run
and the transaction file will be saved astest.tx
ingrin-armor/
. - Import the transaction file into your wallet:
grin-wallet --floonet receive -i /path/to/grin-armor/test.tx
. grin-wallet
will create atest.tx.response
file that you need to armor to return to your counterparty.- Modify the above code to read and armor the response file to give you a copy pastable string
let mut file = File::open("src/test.tx.bin").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let mut slate = slatepack::SerializedSlate::new();
slate.is_bin = true;
slate.bin = buffer;
let armor = slatepack::SerializedSlate::armor(&slate).unwrap();
println!("{}", armor.data);
- You can now copy and paste the armored slate returned in your terminal to your counterparty
- Raw slate data provided by
grin-wallet
must be built as aSerializedSlate
to allow distinguishing json and bin serialization- Convert json string to bytes if necessary and indicate whether the started slate is serialized as binary with
is_bin
- Convert json string to bytes if necessary and indicate whether the started slate is serialized as binary with
- An error checking code is generated by taking the first four bytes of a double sha256 hash of the slate binary
- Error checking code bytes and slate bytes are concatenated
- Output from step 3 is base58 encoded
- Output from step 4 is formatted into 15 character words for readability
- Output from step 5 is framed with a header and footer containing human and machine readable characters, notably a period at the end of the header, a period at the start of the footer and a period at the end of the footer.
- Armor data string is deserialized into bytes
- Bytes are consumed until the first byte representing a "period" is reached- this is the framing header
- Framing header is validated
- Remaining bytes after the framing header are consumed until the next byte representing a "period" is reached- this is the slate body
- Any remaining bytes until the next "period" byte are the framing footer
- Framing footer is validated
- Slate body bytes are sanitized for whitespace characters
- Slate body bytes are decoded with base58
- First four bytes of the result are the error check code
- Remaining slate body bytes are twice hashed with SHA256 and the first four bytes are compared with the error check code from the previous step for validation
- If the validation is successful the slate body bytes should match those from the original slate before it was armored
- Returned value can then be written to a file when using or made ready for transport as a json string when using
remove_armor()
for use ingrin-wallet
- Add support for multiple slatepack messages for very big slates
- Make more idiomatic
- Add examples comparing armor with binary and json serialization
This should not be used for any real transactions. It is for educational purposes to visualize and experiment with the armored slate string format.