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
|
||||
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
|
||||
# and getting results with /get_rpc_result?id=<id>
|
||||
# Dispatcher is dictionary {<method_name>: callable}
|
||||
|
@ -1,10 +1,13 @@
|
||||
from secrets import randbits
|
||||
import base64
|
||||
from time import time
|
||||
|
||||
from typing import Union
|
||||
|
||||
import ujson
|
||||
from onionrblocks import Block
|
||||
import kasten
|
||||
from kasten.generator import pack as kasten_pack
|
||||
|
||||
import onionrblocks
|
||||
from jsonrpc import dispatcher
|
||||
|
||||
@ -67,6 +70,30 @@ def create_and_insert_block(
|
||||
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.
|
||||
# We keep it the same for each daemon life time.
|
||||
queue_to_use = randbits(1)
|
||||
@ -74,8 +101,9 @@ queue_to_use = randbits(1)
|
||||
|
||||
@dispatcher.add_method
|
||||
def insert_block(block: Union[dict, Block]):
|
||||
# Accepts dict because json and accepts block because other functions use it
|
||||
if isinstance(block, dict):
|
||||
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)
|
||||
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