Skip to content

Commit

Permalink
Fix schema for signed queries
Browse files Browse the repository at this point in the history
  • Loading branch information
ZigaMr committed Sep 27, 2024
1 parent 7536053 commit fcb16d9
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 83 deletions.
13 changes: 13 additions & 0 deletions clients/py/sapphirepy/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ def encrypt(self, plaintext: bytes):
}
return cbor2.dumps(envelope, canonical=True)

def encrypt_envelope(self, plaintext: bytes):
ciphertext, nonce = self._encrypt_calldata(plaintext)
envelope = {
'body': {
'pk': self.ephemeral_pubkey,
'data': ciphertext,
'nonce': nonce,
'epoch': self.epoch
},
'format': FORMAT_ENCRYPTED_X25519DEOXYSII
}
return envelope

def _decode_inner(self, plaintext:bytes) -> bytes:
inner_result = cast(ResultInner, cbor2.loads(plaintext))
if inner_result.get('ok', None) is not None:
Expand Down
83 changes: 24 additions & 59 deletions clients/py/sapphirepy/sapphire.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def _should_intercept(method: RPCEndpoint, params: tuple[TxParams]):

def _encrypt_tx_params(pk: CalldataPublicKey,
params: tuple[TxParams],
method,
web3: Web3,
account: LocalAccount) -> TransactionCipher:
c = TransactionCipher(peer_pubkey=pk['key'], peer_epoch=pk['epoch'])
Expand All @@ -84,26 +85,26 @@ def _encrypt_tx_params(pk: CalldataPublicKey,
elif isinstance(data, str):
if len(data) < 2 or data[:2] != '0x':
raise ValueError('Data is not hex encoded!', data)
# Testing
data_bytes = unhexlify(data[2:])
else:
raise TypeError("Invalid 'data' type", type(data))
encrypted_data = c.encrypt(data_bytes)

if params[0]['from']: # and params[0]['from'] == account.address:
data_pack = _new_signed_call_data_pack(encrypted_data,
if params[0]['from'] and method == 'eth_call':
data_pack = _new_signed_call_data_pack(c.encrypt_envelope(data_bytes),
data_bytes,
params,
web3,
account)
params[0]['data'] = HexStr('0x' + hexlify(cbor2.dumps(data_pack)).decode('ascii'))
# params[0]['data'] = '0x' + params[0]['data'].hex()
data_pack['signature'] = bytes(data_pack['signature'])
params[0]['data'] = cbor2.dumps(data_pack, canonical=True)
else:
params[0]['data'] = HexStr('0x' + hexlify(encrypted_data).decode('ascii'))

return c


def _new_signed_call_data_pack(encrypted_data: bytes,
def _new_signed_call_data_pack(encrypted_data: dict,
data_bytes: bytes,
params: tuple[TxParams],
web3: Web3,
Expand Down Expand Up @@ -136,14 +137,14 @@ def _new_signed_call_data_pack(encrypted_data: bytes,
],
"Leash": [
{"name": "nonce", "type": "uint64"},
{"name": "block_number", "type": "uint64"},
{"name": "block_hash", "type": "bytes32"},
{"name": "block_range", "type": "uint64"},
{"name": "blockNumber", "type": "uint64"},
{"name": "blockHash", "type": "bytes32"},
{"name": "blockRange", "type": "uint64"},
],
}
nonce = web3.eth.get_transaction_count(params[0]['from'])
block_number = web3.eth.block_number
block_hash = web3.eth.get_block(block_number)['hash'].hex()
block_hash = web3.eth.get_block(block_number-1)['hash'].hex()
msg_data = {
"from": params[0].get('from'),
"to": params[0].get('to'),
Expand All @@ -154,37 +155,12 @@ def _new_signed_call_data_pack(encrypted_data: bytes,
"leash":
{
"nonce": nonce,
"block_number": block_number - 1,
"block_hash": unhexlify(block_hash[2:]),
"block_range": DEFAULT_BLOCK_RANGE,
"blockNumber": block_number - 1,
"blockHash": unhexlify(block_hash[2:]),
"blockRange": DEFAULT_BLOCK_RANGE,
}
}

# Testing
domain_data_test = {
"name": "oasis-runtime-sdk/evm: signed query",
"version": "1.0.0",
"chainId": 0x5aff,
# "verifyingContract": "",
# "salt": "",
}

msg_data_test = {
"from": '0xDce075E1C39b1ae0b75D554558b6451A226ffe00',
# "to": params[0].get('to', '0x'),
"to": '0x595cce2312b7dfb068eb7dbb8c2b0b593b5c8883',
"value": params[0].get('value', 0),
"gasLimit": params[0].get('gas', DEFAULT_GAS_LIMIT),
"gasPrice": params[0].get('gasPrice', DEFAULT_GAS_PRICE),
"data": unhexlify('e21f37ce'),
"leash":
{
"nonce": 0x12,
"blockNumber": 0x1234,
"blockHash": unhexlify('2ec361fee28d09a3ad2c4d5f7f95d409ce2b68c39b5d647edf0ea651e069e4a8'),
"blockRange": 15,
}
}
full_message = {
"types": msg_types,
"primaryType": "Call",
Expand All @@ -196,26 +172,15 @@ def _new_signed_call_data_pack(encrypted_data: bytes,
signed_msg = Account.sign_typed_data(account.key,
full_message=full_message)

# Testing
# signed_msg = Account.sign_typed_data('c07b151fbc1e7a11dff926111188f8d872f62eba0396da97c0a24adb75161750', full_message=full_message)
# signed_msg = Account.sign_typed_data('c07b151fbc1e7a11dff926111188f8d872f62eba0396da97c0a24adb75161750',
# domain_data, msg_types, msg_data)

# leash = {
# "nonce": nonce,
# "block_number": block_number - 1,
# "block_hash": unhexlify(block_hash[2:]),
# "block_range": DEFAULT_BLOCK_RANGE,
# }
leash = msg_data['leash']

class RequestPack(TypedDict):
Data: bytes
Leash: dict
Signature: HexStr

data_pack: RequestPack = {
'data': cbor2.loads(encrypted_data),
leash = {
"nonce": nonce,
"block_number": block_number - 1,
"block_hash": unhexlify(block_hash[2:]),
"block_range": DEFAULT_BLOCK_RANGE,
}

data_pack= {
'data': encrypted_data,
'leash': leash,
'signature': signed_msg['signature'],
}
Expand Down Expand Up @@ -269,7 +234,7 @@ def middleware(method: RPCEndpoint, params: Any) -> RPCResponse:
raise RuntimeError('Could not retrieve callDataPublicKey!')
do_fetch = False

c = _encrypt_tx_params(pk, params, w3, account)
c = _encrypt_tx_params(pk, params, method, w3, account)

# We may encounter three errors here:
# 'core: invalid call format: epoch too far in the past'
Expand Down
49 changes: 25 additions & 24 deletions clients/py/sapphirepy/tests/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ def compiled_test_contract():
import solcx # type: ignore

with open(GREETER_SOL, 'r', encoding='utf-8') as handle:

solcx.set_solc_version(solcx.install_solc())
solcx.install_solc(version='0.8.9')
solcx.set_solc_version('0.8.9')
compiled_sol = solcx.compile_source(
handle.read(),
output_values=['abi', 'bin']
Expand All @@ -51,49 +51,50 @@ class TestEndToEnd(unittest.TestCase):

def setUp(self):
account: LocalAccount = Account.from_key("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") # pylint: disable=no-value-for-parameter
# account2: LocalAccount = Account.from_key("0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d") # pylint: disable=no-value-for-parameter


w3 = Web3(Web3.HTTPProvider('http://localhost:8545'))
# w3 = Web3(Web3.HTTPProvider('https://testnet.sapphire.oasis.io'))
w3.middleware_onion.add(construct_sign_and_send_raw_middleware(account))
self.w3 = w3 = sapphire.wrap(w3, account)

w3.eth.default_account = account.address

iface = compiled_test_contract()

contract = w3.eth.contract(abi=iface['abi'], bytecode=iface['bin'])
contract = w3.eth.contract(abi=iface['abi'], bytecode=iface['bin'], )
tx_hash = contract.constructor().transact({'gasPrice': w3.eth.gas_price})
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)

self.greeter = w3.eth.contract(address=tx_receipt['contractAddress'], abi=iface['abi'])

# def test_viewcall_revert_custom(self):
# with self.assertRaises(ContractCustomError) as cm:
# self.greeter.functions.revertWithCustomError().call()
# data = self.greeter.encodeABI(
# fn_name="MyCustomError", args=["thisIsCustom"]
# )
# self.assertEqual(cm.exception.args[0], data)
#
# def test_viewcall_revert_reason(self):
# with self.assertRaises(ContractLogicError) as cm:
# self.greeter.functions.revertWithReason().call()
# self.assertEqual(cm.exception.message, 'execution reverted: reasonGoesHere')
def test_viewcall_revert_custom(self):
with self.assertRaises(ContractCustomError) as cm:
self.greeter.functions.revertWithCustomError().call()
data = self.greeter.encodeABI(
fn_name="MyCustomError", args=["thisIsCustom"]
)
self.assertEqual(cm.exception.args[0], data)

def test_viewcall_revert_reason(self):
with self.assertRaises(ContractLogicError) as cm:
self.greeter.functions.revertWithReason().call()
self.assertEqual(cm.exception.message, 'execution reverted: reasonGoesHere')

def test_viewcall(self):
self.assertEqual(self.greeter.functions.greet().call(), 'Hello')

def test_viewcall_only_owner(self):
self.assertEqual(self.greeter.functions.greetOnlyOwner().call(), 'Hello')

# def test_transaction(self):
# w3 = self.w3
# greeter = self.greeter
#
# x = self.greeter.functions.blah().transact({'gasPrice': w3.eth.gas_price})
# y = w3.eth.wait_for_transaction_receipt(x)
# z = greeter.events.Greeting().process_receipt(y)
# self.assertEqual(z[0].args['g'], 'Hello')
def test_transaction(self):
w3 = self.w3
greeter = self.greeter

x = self.greeter.functions.blah().transact({'gasPrice': w3.eth.gas_price})
y = w3.eth.wait_for_transaction_receipt(x)
z = greeter.events.Greeting().process_receipt(y)
self.assertEqual(z[0].args['g'], 'Hello')

if __name__ == '__main__':
unittest.main()

0 comments on commit fcb16d9

Please sign in to comment.