Adding logic to create blocks over RPC while allowing client to do VDF compute
This commit is contained in:
parent
8e5a5854e5
commit
ae41fb1b72
@ -89,7 +89,7 @@ class OnionrRPC(object):
|
|||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def rpc(self):
|
def rpc(self):
|
||||||
# Basic RPC, intended for small amounts of work
|
# Basic RPC, intended for work that won't block very long
|
||||||
# Use /queue_rpc for large workloads like creating blocks
|
# Use /queue_rpc for large workloads like creating blocks
|
||||||
# and getting results with /get_rpc_result?id=<id>
|
# and getting results with /get_rpc_result?id=<id>
|
||||||
# Dispatcher is dictionary {<method_name>: callable}
|
# Dispatcher is dictionary {<method_name>: callable}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
from secrets import randbits
|
from secrets import randbits
|
||||||
import base64
|
import base64
|
||||||
|
from time import time
|
||||||
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import ujson
|
|
||||||
from onionrblocks import Block
|
from onionrblocks import Block
|
||||||
|
import kasten
|
||||||
|
from kasten.generator import pack as kasten_pack
|
||||||
|
|
||||||
import onionrblocks
|
import onionrblocks
|
||||||
from jsonrpc import dispatcher
|
from jsonrpc import dispatcher
|
||||||
|
|
||||||
@ -67,6 +70,30 @@ def create_and_insert_block(
|
|||||||
return bl
|
return bl
|
||||||
|
|
||||||
|
|
||||||
|
@dispatcher.add_method
|
||||||
|
def prepare_block_for_vdf(block_data: 'base64', block_type, ttl: int, metadata: dict):
|
||||||
|
# This allows for untrusted clients to create blocks, they just have to compute the VDF
|
||||||
|
metadata['ttl'] = ttl
|
||||||
|
kasten_packed = kasten_pack.pack(block_data, block_type, metadata, int(time()))
|
||||||
|
kasten_obj = kasten.Kasten('', kasten_packed, kasten.generator.KastenBaseGenerator, auto_check_generator=False)
|
||||||
|
return {
|
||||||
|
'raw': base64.b64encode(kasten_obj).decode('utf-8'),
|
||||||
|
'rounds_needed': onionrblocks.blockcreator.anonvdf.AnonVDFGenerator.get_rounds_for_ttl_seconds(ttl, len(kasten_packed))
|
||||||
|
}
|
||||||
|
|
||||||
|
@dispatcher.add_method
|
||||||
|
def assemble_and_insert_block(
|
||||||
|
kasten_packed: 'base64', vdf_result: 'base64') -> str:
|
||||||
|
bl = onionrblocks.Block(
|
||||||
|
base64.b64decode(vdf_result),
|
||||||
|
base64.b64decode(kasten_packed), auto_verify=True)
|
||||||
|
insert_block(bl)
|
||||||
|
return {
|
||||||
|
'id': bl.id,
|
||||||
|
'raw': base64.b64encode(bl.raw).decode('utf-8')
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# As per dandelion++ spec the edge should be the same.
|
# As per dandelion++ spec the edge should be the same.
|
||||||
# We keep it the same for each daemon life time.
|
# We keep it the same for each daemon life time.
|
||||||
queue_to_use = randbits(1)
|
queue_to_use = randbits(1)
|
||||||
@ -74,8 +101,9 @@ queue_to_use = randbits(1)
|
|||||||
|
|
||||||
@dispatcher.add_method
|
@dispatcher.add_method
|
||||||
def insert_block(block: Union[dict, Block]):
|
def insert_block(block: Union[dict, Block]):
|
||||||
|
# Accepts dict because json and accepts block because other functions use it
|
||||||
if isinstance(block, dict):
|
if isinstance(block, dict):
|
||||||
block = Block(
|
block = Block(
|
||||||
block['id'], base64.b64decode(block['raw']), auto_verify=False)
|
block['id'], base64.b64decode(block['raw']), auto_verify=True)
|
||||||
gossip_block_queues[queue_to_use].put_nowait(block)
|
gossip_block_queues[queue_to_use].put_nowait(block)
|
||||||
return "ok"
|
return "ok"
|
||||||
|
81
tests/default-plugin-tests/rpc/test_insert_block.py
Normal file
81
tests/default-plugin-tests/rpc/test_insert_block.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import os, uuid
|
||||||
|
import base64
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
import time
|
||||||
|
from nacl import signing
|
||||||
|
|
||||||
|
import kasten
|
||||||
|
|
||||||
|
TEST_DIR = 'testdata/%s-%s' % (str(uuid.uuid4())[:6], os.path.basename(__file__)) + '/'
|
||||||
|
print("Test directory:", TEST_DIR)
|
||||||
|
os.environ["ONIONR_HOME"] = TEST_DIR
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append('static-data/official-plugins/rpc/rpc')
|
||||||
|
sys.path.append("src/")
|
||||||
|
|
||||||
|
import queue
|
||||||
|
|
||||||
|
import onionrblocks
|
||||||
|
|
||||||
|
from gossip import blockqueues
|
||||||
|
|
||||||
|
|
||||||
|
import blocks
|
||||||
|
|
||||||
|
class MockQueue:
|
||||||
|
def __init__(self):
|
||||||
|
self.data = []
|
||||||
|
def get_nowait(self):
|
||||||
|
return self.data.pop(0)
|
||||||
|
|
||||||
|
def put_nowait(self, data):
|
||||||
|
print("putting", data)
|
||||||
|
self.data.append(data)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RPCInsertBlockTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_insert_block_dict_valid(self):
|
||||||
|
bl = onionrblocks.create_anonvdf_block(b'test', 'test', 3600)
|
||||||
|
insert_data = {
|
||||||
|
'id': bl.id,
|
||||||
|
'raw': base64.b64encode(bl.raw).decode('utf-8')
|
||||||
|
}
|
||||||
|
assert blocks.insert_block(insert_data) == "ok"
|
||||||
|
try:
|
||||||
|
blockqueues.gossip_block_queues[0].get_nowait()
|
||||||
|
except queue.Empty:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
bl = blockqueues.gossip_block_queues[1].get_nowait()
|
||||||
|
|
||||||
|
def test_insert_block_dict_invalid(self):
|
||||||
|
bl = onionrblocks.create_anonvdf_block(b'test', 'test', 3600)
|
||||||
|
insert_data = {
|
||||||
|
'id': secrets.token_hex(len(bl.id)),
|
||||||
|
'raw': base64.b64encode(bl.raw).decode('utf-8')
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
blocks.insert_block(insert_data)
|
||||||
|
except kasten.exceptions.InvalidID:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
blockqueues.gossip_block_queues[0].get_nowait()
|
||||||
|
except queue.Empty:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise AssertionError("Block was inserted")
|
||||||
|
try:
|
||||||
|
blockqueues.gossip_block_queues[1].get_nowait()
|
||||||
|
except queue.Empty:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise AssertionError("Block was inserted")
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user