Refine ffi interface for python/cpp

This commit is contained in:
J. Ayo Akinyele 2019-08-28 02:17:12 -04:00
parent c947a446ea
commit ef5abccfab
7 changed files with 292 additions and 139 deletions

View File

@ -32,12 +32,13 @@ doc:
pythontests:
cargo +nightly build --release
python py/libbolt.py
python py/tests.py
cpptests:
@cargo +nightly build
@g++ src/main.cpp -L ./target/debug/ -lbolt -I ./include -o cpp_test
@LD_LIBRARY_PATH=./target/debug/ ./cpp_test
@rm cpp_test
@cargo +nightly build --release
@g++ src/main.cpp -L ./target/release/ -lbolt -I ./include -o cpp_test
@LD_LIBRARY_PATH=./target/release/ ./cpp_test
# @rm cpp_test
clean:
cargo +nightly clean

View File

@ -93,19 +93,19 @@ The first part of setting up bi-directional payment channels involve generating
### Initialization
To initialize wallet/keys for both parties, call the ``bidirectional::init_merchant()`` and ``bidirectional::init_customer()``:
To initialize state/keys for both parties, call the ``bidirectional::init_merchant()`` and ``bidirectional::init_customer()``:
let b0_merch = 10;
let b0_cust = 100;
// initialize the merchant wallet and initialize with balance
let (mut channel_token, mut merch_wallet) = bidirectional::init_merchant(rng, &mut channel_state, "Bob");
// initialize the merchant state and initialize with balance
let (mut channel_token, mut merch_state) = bidirectional::init_merchant(rng, &mut channel_state, "Bob");
// initialize the balance for merch_wallet
merch_wallet.init_balance(b0_merch);
// initialize the balance for merch_state
merch_state.init_balance(b0_merch);
// generate the customer wallet using the channel token from the merchant
let mut cust_wallet = bidirectional::init_customer(rng, // rng
// generate the customer state using the channel token from the merchant
let mut cust_state = bidirectional::init_customer(rng, // rng
&mut channel_state, // channel state
&mut channel_token, // channel token
b0_cust, // init customer balance
@ -117,22 +117,22 @@ To initialize wallet/keys for both parties, call the ``bidirectional::init_merch
When opening a payment channel, execute the establishment protocol API to escrow funds privately as follows:
// establish the channel by generating initial wallet commitment proof
let (com, com_proof) = bidirectional::establish_customer_generate_proof(rng, &mut channel_token, &mut cust_wallet);
// establish the channel by generating initial state commitment proof
let (com, com_proof) = bidirectional::establish_customer_generate_proof(rng, &mut channel_token, &mut cust_state);
// obtain close token for closing out channel
let close_token = bidirectional::establish_merchant_issue_close_token(rng, &channel_state, &com, &com_proof, &merch_wallet);
let close_token = bidirectional::establish_merchant_issue_close_token(rng, &channel_state, &com, &com_proof, &merch_state);
// customer verifies that close-token
assert!(cust_wallet.verify_close_token(&channel_state, &close_token));
assert!(cust_state.verify_close_token(&channel_state, &close_token));
// form funding tx and wait for network confirmation
// obtain payment token after confirming funding tx
let pay_token = bidirectional::establish_merchant_issue_pay_token(rng, &channel_state, &com, &merch_wallet);
let pay_token = bidirectional::establish_merchant_issue_pay_token(rng, &channel_state, &com, &merch_state);
// customer
assert!(bidirectional::establish_final(&mut channel_state, &mut cust_wallet, &pay_token));
assert!(bidirectional::establish_final(&mut channel_state, &mut cust_state, &pay_token));
// confirm that the channel state is now established
assert!(channel_state.channel_established);
@ -141,29 +141,29 @@ When opening a payment channel, execute the establishment protocol API to escrow
To spend on the channel, execute the pay protocol API (can be executed as many times as necessary):
// phase 1 - payment proof and new cust wallet
let (payment, new_cust_wallet) = bidirectional::generate_payment_proof(rng, &channel_state, &cust_wallet, 10);
// phase 1 - payment proof and new cust state
let (payment, new_cust_state) = bidirectional::generate_payment_proof(rng, &channel_state, &cust_state, 10);
// phase 1 - merchant verifies the payment proof and returns a close-token
let new_close_token = bidirectional::verify_payment_proof(rng, &channel_state, &payment, &mut merch_wallet);
let new_close_token = bidirectional::verify_payment_proof(rng, &channel_state, &payment, &mut merch_state);
// phase 2 - verify the close-token, update cust wallet and generate a revoke token for previous cust wallet state
let revoke_token = bidirectional::generate_revoke_token(&channel_state, &mut cust_wallet, new_cust_wallet, &new_close_token);
// phase 2 - verify the close-token, update cust state and generate a revoke token for previous cust state state
let revoke_token = bidirectional::generate_revoke_token(&channel_state, &mut cust_state, new_cust_state, &new_close_token);
// phase 2 - merchant verifies the revoke token and sends back the pay-token in response
let new_pay_token = bidirectional::verify_revoke_token(&revoke_token, &mut merch_wallet);
let new_pay_token = bidirectional::verify_revoke_token(&revoke_token, &mut merch_state);
// final - customer verifies the pay token and updates internal state
assert!(cust_wallet.verify_pay_token(&channel_state, &new_pay_token));
assert!(cust_state.verify_pay_token(&channel_state, &new_pay_token));
### Channel Closure Algorithms
To close a channel, the customer must execute the `bidirectional::customer_refund()` routine as follows:
let cust_close_msg = bidirectional::customer_refund(&channel_state, &cust_wallet);
let cust_close_msg = bidirectional::customer_refund(&channel_state, &cust_state);
If the customer broadcasts an outdated version of his wallet, then the merchant can dispute this claim by executing the `bidirectional::merchant_retute()` routine as follows:
If the customer broadcasts an outdated version of his state, then the merchant can dispute this claim by executing the `bidirectional::merchant_retute()` routine as follows:
let merch_close = bidirectional::merchant_refute(&channel_state, &channel_token, &cust_close_msg, &revoke_token);
@ -195,12 +195,12 @@ The channel establishment still works as described before and the pay protocol i
let payment_amount = 20;
// get payment proof on first channel with party A (and I)
let (payment_proofA, new_cust_walletA) = bidirectional::generate_payment_proof(rng, &channel_a, // channel state
&cust_walletA,
let (payment_proofA, new_cust_stateA) = bidirectional::generate_payment_proof(rng, &channel_a, // channel state
&cust_stateA,
payment_amount); // bal inc
// get payment proof on second channel with party B (and I)
let (payment_proofB, new_cust_walletB) = bidirectional::generate_payment_proof(rng, &channel_b,
&cust_walletB,
let (payment_proofB, new_cust_stateB) = bidirectional::generate_payment_proof(rng, &channel_b,
&cust_stateB,
-payment_amount); // bal dec
// verify that the payment proof is valid and cancels out or results in a fee

View File

@ -11,11 +11,9 @@ using namespace rapidjson;
extern "C" {
char* ffishim_validate_channel_open(const char *tc_c, const char *m);
char* ffishim_validate_channel_close(const char *pp, const char *rc_c, const char *pk_m);
char* ffishim_resolve_channel_dispute(const char *pp, const char *rc_c, const char *tc_c, const char *rc_m, const char *pk_m);
char* ffishim_bidirectional_wtp_check_wpk(const char *wpk);
char* ffishim_bidirectional_wtp_verify_cust_close_message(const char *channel_token, const char *wpk, const char *cust_close, const char *close_token);
char *ffishim_bidirectional_wtp_verify_merch_close_message(const char *channel_token, const char *wpk, const char *merch_close);
const char* string_replace_all(const char* previous_string, char old_char, char new_char)
{
@ -26,24 +24,34 @@ extern "C" {
while ((index = s.find(old_c)) != std::string::npos) {
s.replace(index, 1, new_c);
}
printf("STRING: %s\n", s.c_str());
return s.c_str();
}
/* Purpose: open channel
* Arguments: take as input the channel token which consists of the serialized wallet commitment,
* commitment pub params and a second arg (serialized vector of messages m).
int wtp_check_wpk(const char *wpk)
{
const char *ret = ffishim_bidirectional_wtp_check_wpk(wpk);
printf("RESULT: %s\n", ret);
return 0;
}
/* Purpose: verify cust close message
* Arguments: take as input the channel token and wpk
*
* Returns: 0 (false) or 1 (true)
*/
int validate_channel_open(const char *tc_c, const char *m)
int wtp_verify_cust_close_message(const char *channel_token, const char *wpk, const char *cust_close, const char *close_token)
{
// Call into Rust
const char* return_json = string_replace_all(ffishim_validate_channel_open(tc_c, m), '\'', '\"');
// Call rust
const char *return_json = ffishim_bidirectional_wtp_verify_cust_close_message(channel_token, wpk, cust_close, close_token);
Document d;
d.Parse(return_json);
// Make sure we arent going to get an error when indexing into the JSON
assert(d.HasMember("return_value"));
Value& s = d["return_value"];
assert(d.HasMember("result"));
Value& s = d["result"];
// If the return_value is true, then return 1. Otherwise, just assume 0
if( std::string(s.GetString()).compare(std::string("true")) == 0)
@ -53,21 +61,21 @@ extern "C" {
return 0;
}
/* Purpose: close channel
/* Purpose: verify merch close message
* Arguments: take as input the master pub params for CL (pp), serialized channel closure message (rc_c),
* and serialized public key of merchant.
*
* Returns: 0 (false) or 1 (true)
*/
int validate_channel_close(const char *pp, const char *rc_c, const char *pk_m)
int wtp_verify_merch_close_message(const char *channel_token, const char *wpk, const char *merch_close)
{
// Call into Rust
const char* return_json = string_replace_all(ffishim_validate_channel_close(pp, rc_c, pk_m), '\'', '\"');
const char* return_json = string_replace_all(ffishim_bidirectional_wtp_verify_merch_close_message(channel_token, wpk, merch_close), '\'', '\"');
Document d;
d.Parse(return_json);
// Make sure we arent going to get an error when indexing into the JSON
assert(d.HasMember("return_value"));
Value& s = d["return_value"];
assert(d.HasMember("result"));
Value& s = d["result"];
// If the return_value is true, then return 1. Otherwise, just assume 0
if( std::string(s.GetString()).compare(std::string("true")) == 0)
@ -77,11 +85,5 @@ extern "C" {
return 0;
}
/* Purpose: dispute channel
* Arguments: serialized pub params, channel closure for cust/merch and channel tokens for cust/merch
* Returns: 0 (false) or 1 (true)
* // TODO: figure out how to return the approp balance for cust/merch instead of true or false
*/
int resolve_channel_dispute(const char *pp, const char *rc_c, const char *tc_c, const char *rc_m, const char *pk_m);
}
#endif // LIBBOLT_INCLUDE_H_

View File

@ -103,22 +103,24 @@ class Libbolt(object):
def bidirectional_establish_merchant_issue_close_token(self, channel_state, com, com_proof, init_cust, init_merch, merch_state):
output_string = self.lib.ffishim_bidirectional_establish_merchant_issue_close_token(channel_state.encode(), com.encode(), com_proof.encode(), init_cust, init_merch, merch_state.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary['close_token']
return output_dictionary.get('close_token')
def bidirectional_establish_merchant_issue_pay_token(self, channel_state, com, merch_state):
output_string = self.lib.ffishim_bidirectional_establish_merchant_issue_pay_token(channel_state.encode(), com.encode(), merch_state.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary['pay_token']
return output_dictionary.get('pay_token')
def bidirectional_establish_customer_verify_close_token(self, channel_state, cust_state, close_token):
output_string = self.lib.ffishim_bidirectional_verify_close_token(channel_state.encode(), cust_state.encode(), close_token.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary['is_token_valid'], output_dictionary['channel_state'], output_dictionary['cust_state']
is_token_valid = self._convert_boolean(output_dictionary.get('is_token_valid'))
return is_token_valid, output_dictionary.get('channel_state'), output_dictionary.get('cust_state')
def bidirectional_establish_customer_final(self, channel_state, cust_state, pay_token):
output_string = self.lib.ffishim_bidirectional_establish_customer_final(channel_state.encode(), cust_state.encode(), pay_token.encode())
output_dictionary = ast.literal_eval(ctypes.cast(output_string, ctypes.c_char_p).value.decode('utf-8'))
return output_dictionary['is_established'], output_dictionary['channel_state'], output_dictionary['cust_state']
is_established = self._convert_boolean(output_dictionary.get('is_established'))
return is_established, output_dictionary.get('channel_state'), output_dictionary.get('cust_state')
# PAY PROTOCOL
@ -194,6 +196,14 @@ class Libbolt(object):
def _interperate_json_string_as_dictionary(self, json_string):
return ast.literal_eval(json_string)
def _convert_boolean(self, bool_str):
if bool_str == "true":
return True
if bool_str == "false":
return False
return bool_str
if platform == 'darwin':
prefix = 'lib'
ext = 'dylib'
@ -227,6 +237,7 @@ def run_unit_test():
print("cust_state: ", len(cust_state))
(channel_token, cust_state, com, com_proof) = libbolt.bidirectional_establish_customer_generate_proof(channel_token, cust_state)
print("channel token: => ", channel_token)
print("com: ", com)
close_token = libbolt.bidirectional_establish_merchant_issue_close_token(channel_state, com, com_proof, b0_cust, b0_merch, merch_state)
@ -310,7 +321,6 @@ def run_unit_test():
print("Merchant close msg valid: ", merch_close_valid)
print("<========================================>")
print("<========================================>")
wpk, cust_close_wallet = libbolt.wtp_get_wallet(cust_state)
print("wpk = ", wpk)

116
py/tests.py Normal file
View File

@ -0,0 +1,116 @@
import unittest
import libbolt
import ast, random, json
def rand_hex(stringLength=10):
"""Generate a random hex string of fixed length """
hex_letters = '0123456789abcdef'
return ''.join(random.choice(hex_letters) for i in range(stringLength))
def malformed_token(token):
token_dict = ast.literal_eval(token)
updated_token = {}
for k,v in token_dict.items():
updated_token[k] = rand_hex(1) + v[1:]
return json.dumps(updated_token)
class BoltEstablishTests(unittest.TestCase):
def setUp(self):
self.bolt = libbolt.Libbolt('target/{}/{}bolt.{}'.format(libbolt.mode, libbolt.prefix, libbolt.ext))
self.channel_state = self.bolt.channel_setup("Test Channel")
self.b0_cust = 1000
self.b0_merch = 100
(self.channel_token, self.merch_state) = self.bolt.bidirectional_init_merchant(self.channel_state, self.b0_merch, "Bob")
(channel_token, self.cust_state) = self.bolt.bidirectional_init_customer(self.channel_state, self.channel_token,
self.b0_cust, self.b0_merch, "Alice")
# generate some bad stuff here
larger_b0_cust = 2000
(channel_token_bad, self.cust_state_bad) = self.bolt.bidirectional_init_customer(self.channel_state, self.channel_token,
larger_b0_cust, self.b0_merch, "Alice")
# set them
self.channel_token = channel_token
self.channel_token_bad = channel_token_bad
def test_establish_works_okay(self):
"""
Establish protocol common case works
"""
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(self.channel_state, com, com_proof, self.b0_cust, self.b0_merch, self.merch_state)
self.assertTrue(close_token is not None)
(is_token_valid, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, cust_state, close_token)
self.assertTrue(is_token_valid)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(channel_state, com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(channel_state, cust_state, pay_token)
self.assertTrue(is_channel_established)
#print("Establish protocol works as expected.")
def test_establish_merchant_issue_close_token_fail_as_expected(self):
"""
Initial com proof fails as expected when commitment opening doesn't match expected initial customer and merchant balances
"""
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token_bad, self.cust_state_bad)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(self.channel_state, com, com_proof, self.b0_cust, self.b0_merch, self.merch_state)
self.assertTrue(close_token is None)
#print("Establish protocol fail works as expected.")
def test_establish_customer_verify_close_token_fail_as_expected(self):
"""
Not-signed close token fails to verify
"""
close_token = json.dumps({"h":"b896166d76a7bd02565b6431dca27da4c290e234edfbca8d9189f78311e18f66a138684c91efdf7fd1c4b192bf27f68e",
"H":"add6c20994749185fb7d44f8f5f1f3dbbcd250e4922a9c6c9017c25dda670d94c4b279b7f0fccd56916bf737a29a1938"})
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
(is_token_valid, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, cust_state, close_token)
self.assertTrue(is_token_valid is False)
def test_establish_merchant_issue_pay_token_fail_as_expected(self):
"""
Specifying a different commitment leads to an invalid pay token as expected
"""
bad_com = json.dumps({"c":"852a57e24a2192e1cea19157e44f92d58369751f2012bc1f4a4312a89a63c74a92a4cb1d362b37ae0eda3b3bd1333502"})
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
close_token = self.bolt.bidirectional_establish_merchant_issue_close_token(self.channel_state, com, com_proof, self.b0_cust, self.b0_merch, self.merch_state)
self.assertTrue(close_token is not None)
(is_token_valid, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_verify_close_token(self.channel_state, cust_state, close_token)
self.assertTrue(is_token_valid)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(channel_state, bad_com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(channel_state, cust_state, pay_token)
self.assertFalse(is_channel_established)
def test_establish_not_complete_without_close_token(self):
"""
Test that missing close token prevents the customer from establishing
"""
(channel_token, cust_state, com, com_proof) = self.bolt.bidirectional_establish_customer_generate_proof(self.channel_token, self.cust_state)
pay_token = self.bolt.bidirectional_establish_merchant_issue_pay_token(self.channel_state, com, self.merch_state)
self.assertTrue(pay_token is not None)
(is_channel_established, channel_state, cust_state) = self.bolt.bidirectional_establish_customer_final(self.channel_state, cust_state, pay_token)
self.assertFalse(is_channel_established)
def test_error_handling_with_serialization(self):
pass
if __name__ == '__main__':
unittest.main()

View File

@ -52,6 +52,19 @@ pub mod ffishim {
};
}
#[no_mangle]
pub extern fn ffishim_bidirectional_wtp_check_wpk(ser_wpk: *mut c_char) -> *mut c_char {
let wpk: secp256k1::PublicKey = deserialize_object(ser_wpk);
println!("Got wpk: {}", wpk);
let res = true;
let ser = ["{\'result\':\'", serde_json::to_string(&res).unwrap().as_str(), "\'}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}
#[no_mangle]
pub extern fn ffishim_bidirectional_channel_setup(channel_name: *const c_char, third_party_support: u32) -> *mut c_char {
let bytes = unsafe { CStr::from_ptr(channel_name).to_bytes() };
@ -374,7 +387,7 @@ pub mod ffishim {
let close_token: bidirectional::Signature<Bls12> = deserialize_object(ser_close_token);
// check the signatures
let token_valid = bidirectional::wtp_verify_cust_close_message(&channel_token, &wpk, &close_msg, &close_token);
let ser = ["{\'result\':\'", serde_json::to_string(&token_valid).unwrap().as_str(), "\'}"].concat();
let ser = ["{\"result\":\"", serde_json::to_string(&token_valid).unwrap().as_str(), "\"}"].concat();
let cser = CString::new(ser).unwrap();
cser.into_raw()
}

View File

@ -1,11 +1,13 @@
#include <iostream>
using namespace std;
#include "libbolt.h"
#include "ChannelToken.h"
#include "PublicKey.h"
#include "PublicParams.h"
#include "Wallet.h"
#include "ChannelClosure.h"
//#include "ChannelToken.h"
//#include "PublicKey.h"
//#include "PublicParams.h"
//#include "Wallet.h"
//#include "ChannelClosure.h"
int main()
{
@ -26,83 +28,92 @@ int main()
// std::cout << "Validate Channel Close Successful" << std::endl;
string channel_token = "{\"pk_c\":\"03495dad4e457a510e11b763a6cf0bc7077d0302ddb219990e4ab9093de4afcd72\",\"pk_m\":\"02cd3a749783f14c190f4739462c79069e93c1a813cf9debbd63285cfbcf1182b7\",\"cl_pk_m\":{\"X\":\"b10567ef46321800829e1b2544798ba6deb6e5c45aa9b75704772452742b7ff8fd98b8dad9c14006322d51108a31cd1218d7d58303a368022e521636332df67ee5b3b6c72136683dd814b7850e71889596dd501ed4f4968c24778189c51cb288\",\"Y\":[\"8d72dce6a8b95fd5b989cc3a273d049189208f487a28fe41e8d3728fd99463891baecd873a8e5e29847e885b460d2f681081dbea0187619450951c31d941f89819ba7802fa847c00125e10985d39a1cdd0b519e85bf1a27f35c5705eb5a08963\",\"b6091734ff6fdae0f8ba058c2b0d60549aa8f6142c7bcdcea34d6691d644ce18b88812355e7a1c7e19b3602e1116e28501aa6d27dd625373eaa663b059338dc7b19a4ea554ca58e2a55ee7e8a9f7337e72e7978f1bf442698f83b616a42b5f71\",\"8ff47a570bf9e3436fb7ea181c7e4f48c3ccaa648349301dea9c30c1e33d59536d37caa16efaff00c3583d9a429418e717f4b5206021f0d76db60e2e5479d76a4d3b45a6236f8fd9b377eba49b96388e82ed14d1affceaa6c7fc0de893bcfe2c\",\"a26171b19b8909e76c1c08663b5eb56d9058fad182e45f2a7f076471c535ae27192c1b0f7e4ba6e281cf414e142d56da15066285fe08d8face07c9802ed09765c9293642ff260a994a7da6efe3790894c0406347d0ab4304607157af7db10ee5\",\"b628e9a220c990155052ce487c3702741292ddbe2d5d14321f5c93de21197aa71b8a81f1e69c50aa73d62682091a137a12042911699d31398bfe73154cf765716c0eb0513456760353f7ed134f213a96bbab8f426452d6314e96372175218b35\"]},\"mpk\":{\"g1\":\"ac39f25d7696c1bb353f7e900542a5f43956c9f0a1d0457a6b49c67327bf05f8451ad387d7d35688f3f2acf076d3feaf\",\"g2\":\"88f59db955b6300db9ad92c66401b7c8702c7519e6e6ef4648b1423798a7edc7d08a2f99c33aa8316e569d34554b76f80cc188e48bb097f9dbc4f08bd64d4a034d0c121f26fbff7b07d46e7cc8d02c9a4cd66158b9159b03e66cc9a69e4366d2\"},\"comParams\":{\"pub_bases\":[\"ac39f25d7696c1bb353f7e900542a5f43956c9f0a1d0457a6b49c67327bf05f8451ad387d7d35688f3f2acf076d3feaf\",\"b553573d6635ce89d5e31b1111b609795ab0f2e0319e0ecef683d48dbd0be05490817d9794e04da1c1b5d13afc374640\",\"8947de6549cd362d623207bb4ff0d619fa6f545f304cc3dbd0d93e26097cd9041e2f775ea0a7c4639b628b117b6e0237\",\"ae2522426706bb6bfe9d9e309772dfa0cc579220034d891eb54aa271fe53bb6885ec797d6e85fb0eb2bef9bf3c1341f5\",\"aead29acb2bd56b576471e1a3d9b9efa176b7cd50f296ad0d955f0865f50f98719b94c36264415df7079f64df73db7ff\",\"a86b8707474607dd0fe98b88afc86a74614a37cd0dc134988bbb311d37ce11f8f8e0b0865a4bf34c86fbbd2de8d4c8a5\"]}}";
string wpk = "\"030546296c540270242bbedf2bc33cf81192eb34476e2b4a50628b47ae0a49b7c8\"";
string cust_close = "{\"pkc\": [11112480445289327290, 9528849229355488622, 9953104196257979404, 2246935562847964698], \"wpk\": [4702510609848536600, 5816347499760806185, 8088553412674051630, 4443768976355663367], \"bc\": 85, \"bm\": 25, \"close\": [13608077342737179488, 9587669233036057730, 7059978277971697719, 7773536913132903437]}";
string close_token = "{\"h\": \"a471351ba5900f99c70a6a4b111cde71df7da79904b7056ee2290d6acf4f9ca449ca85ed4fa987e83f1698c5ec8fea2e\", \"H\": \"82be401e9986c2ad79de5cccdb61506876150bc2cb66c3be5ec74c37d41980bb00fd7cef39afe8e656a65953e2615c5f\"}";
int rc0 = wtp_verify_cust_close_message(channel_token.c_str(), wpk.c_str(), cust_close.c_str(), close_token.c_str());
cout << "WTP cust close message result: " << rc0 << endl;
//Channel Token
//int rc1 = wtp_verify_merch_close_message();
std::string channel_token = "05810405c5562fb520201b61b27a4a1328276535238bd05c48045c90fe4e336b5eab761cbd8a2b018298d1e1842580250aa412b3c3e745282b6fc1d89332572d2bcc110785dde055dd35d5bb91fa43ed4fcf3f69f8051134f0f738a40e3480be301b50b5487f5067665fd9272037fddc9029d60ca694d9340422c5cecc46822b91050d0405a2eb579382919cb178376fbec679af6720cf67e744d93fe43cdf02ba412bc1041f579f2c29c6975e0039d0b98912fcbca4af11b9e997da33f50a84dacc470306dd8a90c94d1130592f3e7d87db7e14b9295ecc9edb2aca593b00ceaa0d00b09a7ac8401ab90bdadd7fd3cf6b9cbdaa91f651668cc6adc65b8ebeece32762bc0406a6e14af00d9dc61117d7f07c2021a3422ac09a8c371377fcb4ea2fe8604a4219c2963c3f2f60c56b3dfab46e03e6917e69026270c5d4b7c533d069b63b3ccb085b141dccd23c3ef76cfcd928bc2a892d28f55e19aea980e4480282e72a5484f9453bea8aade1a3c1cc9711a01bf2c415e9179b7869e059192cad231ae867ad0406b08e977b847a5a887a0ec6e3cd0e95400f011c26e055c9cf941dbc157fb301a0e9e2c369fa744fb8fabf27a2ad559132cb026dc2dc8ba13dc0207d501cd00f042029e9d6f977c8ab6bca652c5e7311bf310f4235e24fce1237e98e42b7d7ca97c149b42c5969f42577566414e574a39cde9be6106a0f706bfe20a9801c592e0402e16db2c17416e48641eb9dff678e252df7739d5d8fa57a90892f269c4dc0bd6dc1a8edb21ce89c19aca06f76d85314b6206d7fa4c805068015fc0511064d3304a6e87378912de1c173adf3922407abec3664cb03e60d53e390ac57bf8b381a6b63c5d23d4d41e17cfe27628328d9c4af9215baddf6ed082f821a9e6344f1718104054e6c0db00bb143aee9fce0f28ba16f554fb5388675059fbe6318ea86645b78814dde89f36a1123e3f77412b8164b3f90c254f38352997c4d35359bd8cd65e201a10de92a7e1639ce4daa6b5bd2f1ba27dc9cb8dc98a2cd04e4c8096c9432d173e65d3eef70b530daca876d836abe5f4bd037f3bc2275529cbe3e3661ea8aac201ed26d3ca410cf9522908b2b2c204c393f2cf98f78a13ec258f8cf81976f52db";
ChannelToken token;
assert(token.fromHex(channel_token));
std::cout << token.toJson() << std::endl;
ChannelToken token_from_json;
assert(token_from_json.fromJson(token.toJson()));
std::cout << token_from_json.toJson() << std::endl;
//Public Key
std::string public_key = "4104131f86dc3cc0f088736f2d1a00e3a812d448a90bc1c5689797a109628764f1c21b64ff783a2b60f4b68e281a947e04a70e38305d1c266badb966189fb9c27ae84104057639b31850fef002f1ea152cdd6c01cfa70fa4c92e1a7a27d1799814383bdf2c96c0f3c0fec4d802f8b658608cd7e0bfd28acb5814412f705eeea209f1bd330441042304ac9deac1fa328ad7080b06efa1a65cab43d20b6b0aa25d999a70f910b8a80c52a226c010c6a5095c2e69f4c79b8740c75c80bd7c636a3a00f4ebbd31d472042fdd8f24a1ec8a02bf658e830508a62ec8ad6fa3df52af4424587742ada8ffa419c081e1274758dc08568b1b7aae5f0bdb5fef04ed8b317063f57978414eb9db04222327bdfb69cb29669bafa50e7b9831225e3e053bbc77f228bc002a2749c5fe179c726c71fa5b6db248ea5228a0b6a6f143cf201b7f8c0ca48ab937be364f850422561e2dada073cffc6cd73115fd42acc9ad82e524f0559a5e795a25f555aa9328128057ff6a8c678926b530a185406bd2f67c70d8c983b582a46473ce16492c04810405a2eb579382919cb178376fbec679af6720cf67e744d93fe43cdf02ba412bc1041f579f2c29c6975e0039d0b98912fcbca4af11b9e997da33f50a84dacc470306dd8a90c94d1130592f3e7d87db7e14b9295ecc9edb2aca593b00ceaa0d00b09a7ac8401ab90bdadd7fd3cf6b9cbdaa91f651668cc6adc65b8ebeece32762bc0406a6e14af00d9dc61117d7f07c2021a3422ac09a8c371377fcb4ea2fe8604a4219c2963c3f2f60c56b3dfab46e03e6917e69026270c5d4b7c533d069b63b3ccb085b141dccd23c3ef76cfcd928bc2a892d28f55e19aea980e4480282e72a5484f9453bea8aade1a3c1cc9711a01bf2c415e9179b7869e059192cad231ae867ad0406b08e977b847a5a887a0ec6e3cd0e95400f011c26e055c9cf941dbc157fb301a0e9e2c369fa744fb8fabf27a2ad559132cb026dc2dc8ba13dc0207d501cd00f042029e9d6f977c8ab6bca652c5e7311bf310f4235e24fce1237e98e42b7d7ca97c149b42c5969f42577566414e574a39cde9be6106a0f706bfe20a9801c592e0402e16db2c17416e48641eb9dff678e252df7739d5d8fa57a90892f269c4dc0bd6dc1a8edb21ce89c19aca06f76d85314b6206d7fa4c805068015fc0511064d3304a6e87378912de1c173adf3922407abec3664cb03e60d53e390ac57bf8b381a6b63c5d23d4d41e17cfe27628328d9c4af9215baddf6ed082f821a9e6344f1710441041dc2e3d4173966de7d1ed5e27bc22fe16c76e5608cee95596ebb60c3f02f6bc90a065eafd105538555a1f4f29fd90ce54472145df3e80e9b997fd98eebcaf3dd042af359d72df9e9652b3032d431810625ba80429d24fb67e878606b5ed890dc0010630af8ee84335853328b988574d48b331db1ba5509bb006b63251ad38548500428c058139c2ef503b78bd7f43d41a3dcd85ee081500a324ceafb05271b4132641a197b773c007a2e5a05b1c241bdbd36bf837bf41f9c53bea7259e2384e31d080427f5183459d41bcd52035895b2a80b327a14b644dfdda3eecffcc0d1d4143bb92453076c1a93e220ee5661d2971d5bdd3c57e3f8c64297823d0f4a0f8968e017";
PublicKey pk_m;
assert(pk_m.fromHex(public_key));
std::cout << pk_m.toJson() << std::endl;
PublicKey pk_m_from_json;
assert(pk_m_from_json.fromJson(pk_m.toJson()));
std::cout << pk_m_from_json.toJson() << std::endl;
//PP
std::string pp_string = "41042b92f850665c7ae404f048e4b606aa188e2fcd911aa081dcd219691da20914c00cbee75ed0de801b1e20166c40964aef1752c4668e4ca7d315f5224f9dbe985f810405c5562fb520201b61b27a4a1328276535238bd05c48045c90fe4e336b5eab761cbd8a2b018298d1e1842580250aa412b3c3e745282b6fc1d89332572d2bcc110785dde055dd35d5bb91fa43ed4fcf3f69f8051134f0f738a40e3480be301b50b5487f5067665fd9272037fddc9029d60ca694d9340422c5cecc46822b91050d";
PublicParams pp;
assert(pp.fromHex(pp_string));
std::cout << pp.toJson() << std::endl;
PublicParams pp_from_json;
assert(pp_from_json.fromJson(pp.toJson()));
std::cout << pp_from_json.toJson() << std::endl;
// Wallet
std::string x = "[[3,57,194,144,135,110,163,182,35,213,188,149,61,173,116,213,111,211,127,132,12,49,29,249,63,164,255,128,237,122,122,250],[47,197,153,89,35,28,236,71,233,146,254,194,211,208,146,152,208,30,74,147,115,64,57,141,31,199,112,188,113,193,196,232],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50],[24,79,61,24,253,183,252,253,186,39,65,174,36,49,35,125,138,69,241,75,91,12,141,247,191,173,213,219,246,133,136,217]]";
Wallet w;
assert(w.fromJson(x));
std::cout << w.toHex() << std::endl;
std::cout << w.toJson() << std::endl;
std::string channel_closure = "810407e742f729215b4aa2456b78aeb502f8153442aa64ebec0f5a468e15bf2c5f2284ede6da4001b4d941745be42985d811d4d435c9d6f09489a18caf9a8ec1fd290125b1085d96535351662a35e09165cbdeb146cc6a49f9772332cbd4c24314d6571ac1f798c2bc2b4824d0bb03396f1be04b84ad6a7a51cc29833e8501cc3a98810407537ad2085a8c5a30825414f494360b81b773f31a4a646eee07725cfd9b12a70593a40ecaa54bce6f7668f66373967dde8cd0d77298706175d2f2130899e2fa06072488da670045ebf4b63cf4a8a8691fe26df666f278baa82aebbcd480c753e60363e94736bd7fdc9963afe30c60aec03e198ef7c7a6a8d2125db178abf772810403aa86d5f86a3b6a65800ae7568d39b69591ce855a435d7f5e9b1d3975664e605c70e4289d3a9ba7479c190b4ceb924b35be672d0ec6bb806b6faa7fc658dd9101da26bdfd5c19b95377b3edf62e24164650b32bb76db9173c21e59264d55b561cd89daa2083d4591205a250b742bc116bb555fe2d0837df998c6813f022dbfd048104062caa84a96615f0e1c11b37fe9919b76fb2ce108b88d37493df0f305cb52055105021231af48f935132252158203f85c075dbc4e1009356263bc6d08318a08f07bc4eb2da837ee89ddf91ff0fac086b7b0338cf8a7b1f00bf138fd66da5f21317b506a152f9aaba519e2a3037318bdbaab41121508b83b22b1de0f6e4db7d4e04035247d98e1fd1b330d4ebb5ccac8f7743b4a1d5e4bfbe95cb5e62b4ee2268cc1f2ed510489a9163ec76206f73a990505fe1d2dc9e1368f7259e2e764477d76a07cbec4d14ce7c0d727484389e25545a29790ccbc57a3c77ad173587c3bec6e2ef09022bc375ec60d929e7358c8ac33394f22d4318bb9b0065eef3a76bfaef830408c4f122a13aa0f10a0831fcabffbf63de2d9209e209723ea26a32e671fabfdd1f35bac4fcdf7a47780fe687f17cb3563df2448bb1f00073b223ea19a3eb9b5901358222c0244e10dee16ff87fe3ad405b740e9e3580235bffccd2f25fed9894073c5d06f0e057fe47a4c44368c620021c9f36e338125b188ed5938c129f31e60401a2ea8625fc205c2f17ca9bd1473c95474d39d9e96107a0f61a1bb79ae9e517413b62f769e8e1afd66b6913f0307060400cfd82a1661c963810f2db3bebeb4c030a79b4531197b8663027e07348b04193419305093927b9cb3fff49be638a4b35a4424b74614e5fdb3ac2dd87654c60fb73e9e1ec6e2bdf5192ec4aa1581d4b04810406e45ea5e454d881c7ee2a343c880e1aa384c3889500d8339de474c8f704e844e48cad63823b3138a37c8395e1899b36697035ad8adb032eec90e1a6d131063e02c429ebe47f8578cc0a60bc7d59dffc3ce54c1d7fdae7f47838a32baee6951b6ca7ddb6ebcff21cbae42ab915eeccb86ebd720a91f8f9d304fd9ebc5f03fd65040427f35fe2449f1df1895bcb85fd5b48966edcf410be958ed3208503c593ae24b122a2db2fe8f6cbeca6fe760d8fece344e2c59ea21f5f8378ee426255fed66f03742c6af1e7ef82c2d980feee6ba262673a02d5eda8fb5d5d64c7396e9cdd108dbf3bf23bca77975a8dbfbfa7f50021f5a952bf294b45ad50ae079161fecb1a04034520aa85afbfab4be559a163a6c4b9b6fb0f9eb8eeac0915a5511d5715f24f9c5791041a9f9ac9de9a0e2bb43c957d3024bb2e894e6c838a0c3562c4cbe09f06962003038e15d2063779cdd70e3bd110eb1880c0ac97039855a9daa12d5f1ddcad1824c0b592c16b5fed522f445ad45ce7de8fc86e0c9b94189b4b51bea5b004025b30cada48b99cb79891bbe84c352d562319ef5712d66a84e08f44ff785131339d98c0a069e598d192d10a21c457edacedea23f618ddb37e63319674ed3f5400fc4ccdd8724d047bfd9565d5bd0dd8803fb3049a3541a427f760690f4feac4259b2a4a69e1f5471af6b7223a6ab407ca697fb9b5cae47e6e28bae6f412cf7a";
ChannelClosure c;
assert(c.fromHex(channel_closure));
std::cout << c.toJson() << std::endl;
ChannelClosure c_from_json;
assert(c_from_json.fromJson(c.toJson()));
std::cout << c_from_json.toJson() << std::endl;
// //Channel Token
//
// std::string channel_token = "05810405c5562fb520201b61b27a4a1328276535238bd05c48045c90fe4e336b5eab761cbd8a2b018298d1e1842580250aa412b3c3e745282b6fc1d89332572d2bcc110785dde055dd35d5bb91fa43ed4fcf3f69f8051134f0f738a40e3480be301b50b5487f5067665fd9272037fddc9029d60ca694d9340422c5cecc46822b91050d0405a2eb579382919cb178376fbec679af6720cf67e744d93fe43cdf02ba412bc1041f579f2c29c6975e0039d0b98912fcbca4af11b9e997da33f50a84dacc470306dd8a90c94d1130592f3e7d87db7e14b9295ecc9edb2aca593b00ceaa0d00b09a7ac8401ab90bdadd7fd3cf6b9cbdaa91f651668cc6adc65b8ebeece32762bc0406a6e14af00d9dc61117d7f07c2021a3422ac09a8c371377fcb4ea2fe8604a4219c2963c3f2f60c56b3dfab46e03e6917e69026270c5d4b7c533d069b63b3ccb085b141dccd23c3ef76cfcd928bc2a892d28f55e19aea980e4480282e72a5484f9453bea8aade1a3c1cc9711a01bf2c415e9179b7869e059192cad231ae867ad0406b08e977b847a5a887a0ec6e3cd0e95400f011c26e055c9cf941dbc157fb301a0e9e2c369fa744fb8fabf27a2ad559132cb026dc2dc8ba13dc0207d501cd00f042029e9d6f977c8ab6bca652c5e7311bf310f4235e24fce1237e98e42b7d7ca97c149b42c5969f42577566414e574a39cde9be6106a0f706bfe20a9801c592e0402e16db2c17416e48641eb9dff678e252df7739d5d8fa57a90892f269c4dc0bd6dc1a8edb21ce89c19aca06f76d85314b6206d7fa4c805068015fc0511064d3304a6e87378912de1c173adf3922407abec3664cb03e60d53e390ac57bf8b381a6b63c5d23d4d41e17cfe27628328d9c4af9215baddf6ed082f821a9e6344f1718104054e6c0db00bb143aee9fce0f28ba16f554fb5388675059fbe6318ea86645b78814dde89f36a1123e3f77412b8164b3f90c254f38352997c4d35359bd8cd65e201a10de92a7e1639ce4daa6b5bd2f1ba27dc9cb8dc98a2cd04e4c8096c9432d173e65d3eef70b530daca876d836abe5f4bd037f3bc2275529cbe3e3661ea8aac201ed26d3ca410cf9522908b2b2c204c393f2cf98f78a13ec258f8cf81976f52db";
//
// ChannelToken token;
//
// assert(token.fromHex(channel_token));
//
// std::cout << token.toJson() << std::endl;
//
// ChannelToken token_from_json;
//
// assert(token_from_json.fromJson(token.toJson()));
//
// std::cout << token_from_json.toJson() << std::endl;
//
// //Public Key
//
// std::string public_key = "4104131f86dc3cc0f088736f2d1a00e3a812d448a90bc1c5689797a109628764f1c21b64ff783a2b60f4b68e281a947e04a70e38305d1c266badb966189fb9c27ae84104057639b31850fef002f1ea152cdd6c01cfa70fa4c92e1a7a27d1799814383bdf2c96c0f3c0fec4d802f8b658608cd7e0bfd28acb5814412f705eeea209f1bd330441042304ac9deac1fa328ad7080b06efa1a65cab43d20b6b0aa25d999a70f910b8a80c52a226c010c6a5095c2e69f4c79b8740c75c80bd7c636a3a00f4ebbd31d472042fdd8f24a1ec8a02bf658e830508a62ec8ad6fa3df52af4424587742ada8ffa419c081e1274758dc08568b1b7aae5f0bdb5fef04ed8b317063f57978414eb9db04222327bdfb69cb29669bafa50e7b9831225e3e053bbc77f228bc002a2749c5fe179c726c71fa5b6db248ea5228a0b6a6f143cf201b7f8c0ca48ab937be364f850422561e2dada073cffc6cd73115fd42acc9ad82e524f0559a5e795a25f555aa9328128057ff6a8c678926b530a185406bd2f67c70d8c983b582a46473ce16492c04810405a2eb579382919cb178376fbec679af6720cf67e744d93fe43cdf02ba412bc1041f579f2c29c6975e0039d0b98912fcbca4af11b9e997da33f50a84dacc470306dd8a90c94d1130592f3e7d87db7e14b9295ecc9edb2aca593b00ceaa0d00b09a7ac8401ab90bdadd7fd3cf6b9cbdaa91f651668cc6adc65b8ebeece32762bc0406a6e14af00d9dc61117d7f07c2021a3422ac09a8c371377fcb4ea2fe8604a4219c2963c3f2f60c56b3dfab46e03e6917e69026270c5d4b7c533d069b63b3ccb085b141dccd23c3ef76cfcd928bc2a892d28f55e19aea980e4480282e72a5484f9453bea8aade1a3c1cc9711a01bf2c415e9179b7869e059192cad231ae867ad0406b08e977b847a5a887a0ec6e3cd0e95400f011c26e055c9cf941dbc157fb301a0e9e2c369fa744fb8fabf27a2ad559132cb026dc2dc8ba13dc0207d501cd00f042029e9d6f977c8ab6bca652c5e7311bf310f4235e24fce1237e98e42b7d7ca97c149b42c5969f42577566414e574a39cde9be6106a0f706bfe20a9801c592e0402e16db2c17416e48641eb9dff678e252df7739d5d8fa57a90892f269c4dc0bd6dc1a8edb21ce89c19aca06f76d85314b6206d7fa4c805068015fc0511064d3304a6e87378912de1c173adf3922407abec3664cb03e60d53e390ac57bf8b381a6b63c5d23d4d41e17cfe27628328d9c4af9215baddf6ed082f821a9e6344f1710441041dc2e3d4173966de7d1ed5e27bc22fe16c76e5608cee95596ebb60c3f02f6bc90a065eafd105538555a1f4f29fd90ce54472145df3e80e9b997fd98eebcaf3dd042af359d72df9e9652b3032d431810625ba80429d24fb67e878606b5ed890dc0010630af8ee84335853328b988574d48b331db1ba5509bb006b63251ad38548500428c058139c2ef503b78bd7f43d41a3dcd85ee081500a324ceafb05271b4132641a197b773c007a2e5a05b1c241bdbd36bf837bf41f9c53bea7259e2384e31d080427f5183459d41bcd52035895b2a80b327a14b644dfdda3eecffcc0d1d4143bb92453076c1a93e220ee5661d2971d5bdd3c57e3f8c64297823d0f4a0f8968e017";
//
// PublicKey pk_m;
//
// assert(pk_m.fromHex(public_key));
//
// std::cout << pk_m.toJson() << std::endl;
//
// PublicKey pk_m_from_json;
//
// assert(pk_m_from_json.fromJson(pk_m.toJson()));
//
// std::cout << pk_m_from_json.toJson() << std::endl;
//
// //PP
//
// std::string pp_string = "41042b92f850665c7ae404f048e4b606aa188e2fcd911aa081dcd219691da20914c00cbee75ed0de801b1e20166c40964aef1752c4668e4ca7d315f5224f9dbe985f810405c5562fb520201b61b27a4a1328276535238bd05c48045c90fe4e336b5eab761cbd8a2b018298d1e1842580250aa412b3c3e745282b6fc1d89332572d2bcc110785dde055dd35d5bb91fa43ed4fcf3f69f8051134f0f738a40e3480be301b50b5487f5067665fd9272037fddc9029d60ca694d9340422c5cecc46822b91050d";
//
// PublicParams pp;
//
// assert(pp.fromHex(pp_string));
//
// std::cout << pp.toJson() << std::endl;
//
// PublicParams pp_from_json;
//
// assert(pp_from_json.fromJson(pp.toJson()));
//
// std::cout << pp_from_json.toJson() << std::endl;
//
// // Wallet
//
// std::string x = "[[3,57,194,144,135,110,163,182,35,213,188,149,61,173,116,213,111,211,127,132,12,49,29,249,63,164,255,128,237,122,122,250],[47,197,153,89,35,28,236,71,233,146,254,194,211,208,146,152,208,30,74,147,115,64,57,141,31,199,112,188,113,193,196,232],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50],[24,79,61,24,253,183,252,253,186,39,65,174,36,49,35,125,138,69,241,75,91,12,141,247,191,173,213,219,246,133,136,217]]";
//
// Wallet w;
//
// assert(w.fromJson(x));
//
// std::cout << w.toHex() << std::endl;
//
// std::cout << w.toJson() << std::endl;
//
// std::string channel_closure = "810407e742f729215b4aa2456b78aeb502f8153442aa64ebec0f5a468e15bf2c5f2284ede6da4001b4d941745be42985d811d4d435c9d6f09489a18caf9a8ec1fd290125b1085d96535351662a35e09165cbdeb146cc6a49f9772332cbd4c24314d6571ac1f798c2bc2b4824d0bb03396f1be04b84ad6a7a51cc29833e8501cc3a98810407537ad2085a8c5a30825414f494360b81b773f31a4a646eee07725cfd9b12a70593a40ecaa54bce6f7668f66373967dde8cd0d77298706175d2f2130899e2fa06072488da670045ebf4b63cf4a8a8691fe26df666f278baa82aebbcd480c753e60363e94736bd7fdc9963afe30c60aec03e198ef7c7a6a8d2125db178abf772810403aa86d5f86a3b6a65800ae7568d39b69591ce855a435d7f5e9b1d3975664e605c70e4289d3a9ba7479c190b4ceb924b35be672d0ec6bb806b6faa7fc658dd9101da26bdfd5c19b95377b3edf62e24164650b32bb76db9173c21e59264d55b561cd89daa2083d4591205a250b742bc116bb555fe2d0837df998c6813f022dbfd048104062caa84a96615f0e1c11b37fe9919b76fb2ce108b88d37493df0f305cb52055105021231af48f935132252158203f85c075dbc4e1009356263bc6d08318a08f07bc4eb2da837ee89ddf91ff0fac086b7b0338cf8a7b1f00bf138fd66da5f21317b506a152f9aaba519e2a3037318bdbaab41121508b83b22b1de0f6e4db7d4e04035247d98e1fd1b330d4ebb5ccac8f7743b4a1d5e4bfbe95cb5e62b4ee2268cc1f2ed510489a9163ec76206f73a990505fe1d2dc9e1368f7259e2e764477d76a07cbec4d14ce7c0d727484389e25545a29790ccbc57a3c77ad173587c3bec6e2ef09022bc375ec60d929e7358c8ac33394f22d4318bb9b0065eef3a76bfaef830408c4f122a13aa0f10a0831fcabffbf63de2d9209e209723ea26a32e671fabfdd1f35bac4fcdf7a47780fe687f17cb3563df2448bb1f00073b223ea19a3eb9b5901358222c0244e10dee16ff87fe3ad405b740e9e3580235bffccd2f25fed9894073c5d06f0e057fe47a4c44368c620021c9f36e338125b188ed5938c129f31e60401a2ea8625fc205c2f17ca9bd1473c95474d39d9e96107a0f61a1bb79ae9e517413b62f769e8e1afd66b6913f0307060400cfd82a1661c963810f2db3bebeb4c030a79b4531197b8663027e07348b04193419305093927b9cb3fff49be638a4b35a4424b74614e5fdb3ac2dd87654c60fb73e9e1ec6e2bdf5192ec4aa1581d4b04810406e45ea5e454d881c7ee2a343c880e1aa384c3889500d8339de474c8f704e844e48cad63823b3138a37c8395e1899b36697035ad8adb032eec90e1a6d131063e02c429ebe47f8578cc0a60bc7d59dffc3ce54c1d7fdae7f47838a32baee6951b6ca7ddb6ebcff21cbae42ab915eeccb86ebd720a91f8f9d304fd9ebc5f03fd65040427f35fe2449f1df1895bcb85fd5b48966edcf410be958ed3208503c593ae24b122a2db2fe8f6cbeca6fe760d8fece344e2c59ea21f5f8378ee426255fed66f03742c6af1e7ef82c2d980feee6ba262673a02d5eda8fb5d5d64c7396e9cdd108dbf3bf23bca77975a8dbfbfa7f50021f5a952bf294b45ad50ae079161fecb1a04034520aa85afbfab4be559a163a6c4b9b6fb0f9eb8eeac0915a5511d5715f24f9c5791041a9f9ac9de9a0e2bb43c957d3024bb2e894e6c838a0c3562c4cbe09f06962003038e15d2063779cdd70e3bd110eb1880c0ac97039855a9daa12d5f1ddcad1824c0b592c16b5fed522f445ad45ce7de8fc86e0c9b94189b4b51bea5b004025b30cada48b99cb79891bbe84c352d562319ef5712d66a84e08f44ff785131339d98c0a069e598d192d10a21c457edacedea23f618ddb37e63319674ed3f5400fc4ccdd8724d047bfd9565d5bd0dd8803fb3049a3541a427f760690f4feac4259b2a4a69e1f5471af6b7223a6ab407ca697fb9b5cae47e6e28bae6f412cf7a";
//
// ChannelClosure c;
//
// assert(c.fromHex(channel_closure));
//
// std::cout << c.toJson() << std::endl;
//
// ChannelClosure c_from_json;
//
// assert(c_from_json.fromJson(c.toJson()));
//
// std::cout << c_from_json.toJson() << std::endl;
return 0;
}
}