For me, where i want to get at, is to get a definition of node that would look like this :
{
"node" :
{
"name":"purenode",
"seed_node_host":"iadix.com",
"seed_node_port":16714,
"p2p_port":16819,
"magic":0xD9BEFECA,
"version":60018,
"sign_mod":"ecdsa",
"pubKeyVersion":0x19,
"staking":
{
"staking_kernel":"stake_pos3",
"target spacing":64,
"target timespan":960,
"stake reward":150000000,
"limit":0x1B00FFFF
},
"mining":
{
"target spacing":64,
"target timespan":960,
"limit":0x1E0FFFFF,
"reward":10000000000000,
"last_pow_block":200,
"paytxfee":10000
},
"genesis":
{
"version" :1,
"time" :1466419085,
"bits" :0x1e0fffff,
"nonce" :579883,
"InitialStakeModifier":0,
"InitialStakeModifier2":0
},
"initialize" : function () {}, // initialize the app (constructor) eg load the required modules, allocate memory
"start" : function () {}, //start the application eg open port, initialize protocol and connect nodes
"stop" : function () {}, //stop application eg close port and all connections
"free" : function () {}, //free resources (destructor) unload all modules and free memory
"message_handlers" :
{
//P2P message handlers
{"msg":{"cmd":"inv", function (node,inv_data) { %MyNode%->send_getdata(node,inv_data); } }}, //send get data message to the node sending the inv
{"msg":{"cmd":"block", function (node,block_data) { %MyNode%->validate_block(node,block_data); } } }, // call %MyNode% module method on the data
or implement the block validation protocol
},
//define rpc interface for the wallet
"rpc_wallet":
{
"module":"rpc_wallet",
"rpc_port":16820,
"index":"/wallet",
"page file":"wallet.html"
},
//define http api for block exporer
"block_explorer":
{
"module":"block_explorer",
"base path":"/api/",
"page file":"blocks.html"
}
}
}
To get pedantic on design pattern, the blockchain protocol technically it's already an application layer, and in fact it contain in itself 2 layer, the message layer, and the actual blockchain layer.
The message layer can be seen as the transport layer, it encapsulate the message data, and it's the part that is handled by the node modue, it read packets from the network, and parse the message "transport" layer.
The protocol module is there to deal with the blockchain layer data format, how block headers and transactions and other objects are represented on the network to be transmitted between two nodes. The protocol module contain template like type class to deserialize the message data from the node (transport layer), with run time type definition, in sort that the node can deserialize the message independently of protocol definition. The whole format of the blockchain layer data can be changed, and it wouldn't need to recompile the node module.
The code of the node module is not on the git, but it goes like this
for reading new message from the network
https://github.com/iadix/purenode/blob/master/protocol_adx/protocol.c#L1370OS_API_C_FUNC(int) new_message(const struct string *data, mem_zone_ref_ptr msg)
{
mem_zone_ref_ptr key;
mem_zone_ref list = { PTR_NULL },my_list = { PTR_NULL };
mem_zone_ref payload_node = { PTR_NULL };
struct string pack_str = { PTR_NULL };
size_t cnt = 0;
size_t elSz = 0;
int ret,nc;
if (!strncmp_c(&data->str[4], "version", 7))
make_string(&pack_str, "{(\"payload\",0x0B000010) (0x02)\"proto_ver\" : 0,\"services\" : 0, \"timestamp\" : 0, (0x0B000040)\"their_addr\":\"\", (0x0B000040)\"my_addr\":\"\",\"nonce\":0,(0x0B000100)\"user_agent\":\"\", (0x02)\"last_blk\":0}");
else if (!strncmp_c(&data->str[4], "ping", 4))
make_string(&pack_str, "{(\"payload\",0x0B000010) \"nonce\":0}");
else if (!strncmp_c(&data->str[4], "pong", 4))
make_string(&pack_str, "{(\"payload\",0x0B000010) \"nonce\":0}");
ret=tree_manager_json_loadb (pack_str.str, pack_str.len, &payload_node);
free_string (&pack_str);
if (!ret)
return 0;
tree_manager_set_child_value_str(msg, "cmd" , &data->str[4]);
tree_manager_set_child_value_i32(msg, "size", (*((unsigned int *)(&data->str[16]))));
tree_manager_set_child_value_i32(msg, "sum" , *((unsigned int *)(&data->str[20])));
tree_manager_set_child_value_i32(msg, "cnt" , cnt);
tree_manager_set_child_value_i32(msg, "elSz", elSz);
tree_manager_node_add_child (msg, &payload_node);
code from the node module
if (tree_manager_create_node("emitting", NODE_BITCORE_MSG, &msg))
{
new_message(get_con_lastline(node_con), &msg); // create the message type template based on the message command
tree_manager_node_add_child(node, &msg);
tree_manager_set_child_value_i32(&msg, "recv", pre_read);
tree_manager_set_child_value_i32(&msg, "done", 0);
release_zone_ref(&msg);
}
to unserialize the message based on the runtime type definition :
OS_API_C_FUNC(int) read_node_msg(mem_zone_ref_ptr node)
{
mem_zone_ref msg = { PTR_NULL };
struct con *node_con;
if (!tree_manager_get_child_value_ptr(node, NODE_HASH("p2p_con"), 0, (mem_ptr *)&node_con))return 0;
if (tree_manager_find_child_node(node, NODE_HASH("emitting"), NODE_BITCORE_MSG, &msg))
{
if (!tree_manager_get_child_value_i32(&msg, NODE_HASH("size"), &size))
size = 0;
tree_manager_get_child_value_i32(&msg, NODE_HASH("sum"), &sum);
mbedtls_sha256(get_con_lastline(node_con)->str, size, (unsigned char*)checksum1, 0);
mbedtls_sha256((unsigned char*)checksum1, 32, (unsigned char*)checksum, 0);
if (checksum[0] == sum)
{
unserialize_message(&msg, get_con_lastline(node_con)->str, PTR_NULL); //unserialize the message payload based on the template
tree_manager_set_child_value_i32(&msg, "done", 1);
queue_emitted_message(node, &msg); // add the parsed message in the node's emitted message queue
release_zone_ref(&msg);
con_consume_data(node_con, size);
}
}
and the actual message processing that should be scripted in there
https://github.com/iadix/purenode/blob/master/purenode/main.c#L1419int process_node_messages(mem_zone_ref_ptr node)
{
mem_zone_ref msg_list = { PTR_NULL };
mem_zone_ref_ptr msg = PTR_NULL;
mem_zone_ref my_list = { PTR_NULL };
if (!tree_manager_find_child_node(node, NODE_HASH("emitted queue"), NODE_BITCORE_MSG_LIST, &msg_list))return 0;
for (tree_manager_get_first_child(&msg_list, &my_list, &msg); ((msg != NULL) && (msg->zone != NULL)); tree_manager_get_next_child(&my_list, &msg))
{
char cmd[16];
mem_zone_ref payload_node = { PTR_NULL };
int ret;
if (!tree_manager_get_child_value_str(msg, NODE_HASH("cmd"), cmd, 12, 16))continue;
tree_manager_find_child_node(msg, NODE_HASH("payload"), NODE_BITCORE_PAYLOAD, &payload_node);
ret = handle_message(node, cmd, &payload_node);
tree_manager_set_child_value_i32(msg, "handled", ret);
release_zone_ref(&payload_node);
}
tree_remove_child_by_member_value_dword (&msg_list, NODE_BITCORE_MSG, "handled", 1);
tree_remove_child_by_member_value_lt_dword (&msg_list, NODE_BITCORE_MSG, "recvtime", get_time_c()-100);
release_zone_ref(&msg_list);
return 1;
}
int process_nodes()
{
mem_zone_ref_ptr node = PTR_NULL;
mem_zone_ref my_list = { PTR_NULL };
for (tree_manager_get_first_child(&peer_nodes, &my_list, &node); ((node != NULL) && (node->zone != NULL)); tree_manager_get_next_child(&my_list, &node))
{
process_node_messages(node);
The node defintion in the first block of this post would replace the "coin core" module on the graph (
https://github.com/iadix/purenode/blob/master/purenode/ ), where all the coin logic take place, which is the part to check if block headers fit the difficulty, to compute the difficulty re targeting, check the proof of work or stake, check the transaction inputs, etc , but i think most of this could be wrote in high level language script, all the part about protocol consensus and which block is considered valid could be scripted with the high level node definition, scripting call to modules coded in C for the hard crypto part or list processing.
I already have all the code to implement this with the C modules, and it uses the run time type definition so it should not be too hard to integrate a script system based on the node definition i posted above, and to make a parser to have running full node with parameters and protocol consensus wrote in high level language like this.
The thing with data format definition become really useless, it's mostly useful if you want compile time verification, but anyway you can't have this with js, and C/C++ is still very weak for detecting problem at compile time, and it completely prevent true run time type checking.
And unlike in javascript, all the key and variable definition can be sur typed with built in type identifier, with runtime type checking. So can have more design pattern than with javascript.
As my framework is all based on runtime type definition, it allow for runtime type checking, and more robust design pattern than js, and it's all though from scratch to easily build a script engine on top of it.
Where i want to get at with you is that regarding you want to code a new blockchain protocol , it would probably not be longer to code the script engine based on my framework with already 90% of all basic blockchain related code and javascript html5 app / rpc interface already working, than to start coding a blockchain node from scratch, and like this you can have your blockchain programmed already with the high level language that has all the property you want with pararelization, runtime tpe checking, modularity, design pattern & all. And i'm pretty sure we are after the same thing
With clear language, easy to read, dynamic typing, asynch request, green threading, "hard threading" , javascript/html5 compatible, i'm sure we can get something cool.
The point of doing this script engine is to facilitating programming more complex consensus to have protocol more evolved than only checking pow, and facilitate programming distributed application based on flexible definition of node who can run different modules/class exposed as rpc api.
If the synthax remain compatible with json, maybe it can be used as template to generate js objects and or/ui, or use static html5 pages .
And this kind of scripting could really not take too long to get started from the code i already have. And i'm sure you have plenty of experience and good idea to get to a script language like this, to define node/modules interface and global application logic.
And any blockchain can be programmed with it independently of each other, the script engine should allow to easily write new consensus algorithm without having to change too much the underlying code. And as long as the module dependency chain remain ok, apps using those modules should be compatible with the two blockchain.