Adding logic to create blocks over RPC while allowing client to do VDF compute

This commit is contained in:
Kevin F 2023-04-21 09:08:55 -05:00
parent 8e5a5854e5
commit ae41fb1b72
3 changed files with 112 additions and 3 deletions

View File

@ -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}

View File

@ -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"

View 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()