test_basic.py (13984B)
1 # standard imports 2 import logging 3 import os 4 import unittest 5 6 # external imports 7 from chainlib.eth.tx import TxFactory 8 from chainlib.eth.tx import TxFormat 9 from chainlib.eth.unittest.ethtester import EthTesterCase 10 from chainlib.connection import RPCConnection 11 from chainlib.eth.nonce import RPCNonceOracle 12 from chainlib.eth.address import to_checksum_address 13 from chainlib.eth.tx import receipt 14 from chainlib.eth.gas import Gas 15 from chainlib.eth.gas import OverrideGasOracle 16 from chainlib.eth.contract import ABIContractEncoder 17 from chainlib.eth.contract import ABIContractType 18 from chainlib.jsonrpc import JSONRPCRequest 19 from chainlib.error import JSONRPCException 20 from hexathon import add_0x 21 from hexathon import strip_0x 22 from hexathon import same as same_hex 23 from funga.eth.message import to_validator_message 24 from eth_erc712 import EIP712Domain 25 from eth_erc712 import EIP712DomainEncoder 26 27 script_dir = os.path.realpath(os.path.dirname(__file__)) 28 data_dir = os.path.join(script_dir, '..', 'eth_offline', 'data') 29 30 logging.basicConfig(level=logging.DEBUG) 31 logg = logging.getLogger(__name__) 32 33 hash_of_foo = '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae' 34 hash_of_bar = 'fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9' 35 36 37 class TestOfflineEth(EthTesterCase): 38 39 def setUp(self): 40 super(TestOfflineEth, self).setUp() 41 nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) 42 gas_oracle = OverrideGasOracle(limit=3000000) 43 44 fp = os.path.join(data_dir, 'Offline.bin') 45 f = open(fp, 'r') 46 bytecode = f.read() 47 f.close() 48 49 c = Gas(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle, gas_oracle=gas_oracle) 50 self.conn = RPCConnection.connect(self.chain_spec, 'default') 51 52 data = self.chain_spec.chain_id().to_bytes(32, byteorder='big').hex() 53 (tx_hash, o) = c.create(self.accounts[0], None, 0, data=bytecode + data) 54 r = self.conn.do(o) 55 o = receipt(r) 56 r = self.conn.do(o) 57 self.assertEqual(r['status'], 1) 58 self.address = to_checksum_address(r['contract_address']) 59 self.contract_block_hash = r['block_hash'] 60 logg.debug('smart contract published with hash {} address {}'.format(r, self.address)) 61 62 63 def test_validator(self): 64 n = os.urandom(12) 65 c = TxFactory(self.chain_spec) 66 j = JSONRPCRequest() 67 o = j.template() 68 o['method'] = 'eth_call' 69 enc = ABIContractEncoder() 70 enc.method('isOfflineValid') 71 enc.typ(ABIContractType.ADDRESS) 72 enc.typ(ABIContractType.BYTES) 73 enc.address(strip_0x(self.accounts[0])) 74 enc.bytes(n.hex() + strip_0x(self.accounts[1]) + '666f6f') 75 data = add_0x(enc.get()) 76 tx = c.template(self.accounts[0], self.address) 77 tx = c.set_code(tx, data) 78 o['params'].append(c.normalize(tx)) 79 o['params'].append('latest') 80 o = j.finalize(o) 81 r = self.rpc.do(o) 82 self.assertEqual(int(r, 16), 0) 83 84 v = os.urandom(64) 85 c = TxFactory(self.chain_spec) 86 j = JSONRPCRequest() 87 o = j.template() 88 o['method'] = 'eth_call' 89 enc = ABIContractEncoder() 90 enc.method('isOfflineValid') 91 enc.typ(ABIContractType.ADDRESS) 92 enc.typ(ABIContractType.BYTES) 93 enc.address(strip_0x(self.accounts[2])) 94 enc.bytes(n.hex() + strip_0x(self.accounts[1]) + v.hex()) 95 data = add_0x(enc.get()) 96 tx = c.template(self.accounts[0], self.address) 97 tx = c.set_code(tx, data) 98 o['params'].append(c.normalize(tx)) 99 o['params'].append('latest') 100 o = j.finalize(o) 101 r = self.rpc.do(o) 102 self.assertEqual(int(r, 16), 0) 103 104 n = b'\x00' * 12 105 c = TxFactory(self.chain_spec) 106 j = JSONRPCRequest() 107 o = j.template() 108 o['method'] = 'eth_call' 109 enc = ABIContractEncoder() 110 enc.method('isOfflineValid') 111 enc.typ(ABIContractType.ADDRESS) 112 enc.typ(ABIContractType.BYTES) 113 enc.address(strip_0x(self.accounts[0])) 114 enc.bytes(n.hex() + strip_0x(self.accounts[1]) + v.hex()) 115 data = add_0x(enc.get()) 116 tx = c.template(self.accounts[0], self.address) 117 tx = c.set_code(tx, data) 118 o['params'].append(c.normalize(tx)) 119 o['params'].append('latest') 120 o = j.finalize(o) 121 r = self.rpc.do(o) 122 self.assertEqual(int(r, 16), 0) 123 124 n = os.urandom(12) 125 c = TxFactory(self.chain_spec) 126 j = JSONRPCRequest() 127 o = j.template() 128 o['method'] = 'eth_call' 129 enc = ABIContractEncoder() 130 enc.method('isOfflineValid') 131 enc.typ(ABIContractType.ADDRESS) 132 enc.typ(ABIContractType.BYTES) 133 enc.address(strip_0x(self.accounts[0])) 134 enc.bytes(n.hex() + strip_0x(self.accounts[1]) + v.hex()) 135 data = add_0x(enc.get()) 136 tx = c.template(self.accounts[0], self.address) 137 tx = c.set_code(tx, data) 138 o['params'].append(c.normalize(tx)) 139 o['params'].append('latest') 140 o = j.finalize(o) 141 r = self.rpc.do(o) 142 r = strip_0x(r) 143 self.assertEqual(int(r, 16), 1) 144 145 146 def test_ok_verify(self): 147 beneficiary_bin = bytes.fromhex(strip_0x(self.accounts[2])) 148 msg_nonce = os.urandom(12) 149 msg_bin = os.urandom(64) 150 msg_data = msg_nonce + beneficiary_bin + msg_bin 151 152 sig = self.signer.sign_validator_message(self.accounts[0], self.address, msg_data) 153 sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big') 154 logg.debug('message is {} ({}) signed by {}'.format(msg_data.hex(), len(msg_data), self.accounts[0])) 155 156 c = TxFactory(self.chain_spec) 157 j = JSONRPCRequest() 158 o = j.template() 159 o['method'] = 'eth_call' 160 enc = ABIContractEncoder() 161 enc.method('verifyOfflineRequest') 162 enc.typ(ABIContractType.BYTES) 163 enc.typ(ABIContractType.BYTES) 164 enc.bytes(msg_data.hex()) 165 enc.bytes(sig.hex()) 166 data = add_0x(enc.get()) 167 tx = c.template(self.accounts[0], self.address) 168 tx = c.set_code(tx, data) 169 o['params'].append(c.normalize(tx)) 170 o['params'].append('latest') 171 o = j.finalize(o) 172 173 174 r = self.rpc.do(o) 175 r = strip_0x(r) 176 177 self.assertEqual(int(r, 16), 1) 178 179 180 def test_verify_fail_owner(self): 181 beneficiary_bin = bytes.fromhex(strip_0x(self.accounts[2])) 182 msg_nonce = os.urandom(12) 183 msg_bin = os.urandom(64) 184 msg_data = msg_nonce + beneficiary_bin + msg_bin 185 186 sig = self.signer.sign_validator_message(self.accounts[1], self.address, msg_data) 187 sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big') 188 logg.debug('message is {} signed by {}'.format(msg_data.hex(), self.accounts[1])) 189 190 c = TxFactory(self.chain_spec) 191 j = JSONRPCRequest() 192 o = j.template() 193 o['method'] = 'eth_call' 194 enc = ABIContractEncoder() 195 enc.method('verifyOfflineRequest') 196 enc.typ(ABIContractType.BYTES) 197 enc.typ(ABIContractType.BYTES) 198 enc.bytes(msg_data.hex()) 199 enc.bytes(sig.hex()) 200 data = add_0x(enc.get()) 201 tx = c.template(self.accounts[0], self.address) 202 tx = c.set_code(tx, data) 203 o['params'].append(c.normalize(tx)) 204 o['params'].append('latest') 205 o = j.finalize(o) 206 r = self.rpc.do(o) 207 self.assertEqual(int(r, 16), 0) 208 209 210 def test_execute(self): 211 beneficiary_bin = bytes.fromhex(strip_0x(self.accounts[2])) 212 msg_nonce = os.urandom(12) 213 msg_bin = os.urandom(64) 214 msg_data = msg_nonce + beneficiary_bin + msg_bin 215 216 sig = self.signer.sign_validator_message(self.accounts[0], self.address, msg_data) 217 sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big') 218 logg.debug('message is {} signed by {}'.format(msg_data.hex(), self.accounts[1])) 219 220 c = TxFactory(self.chain_spec) 221 j = JSONRPCRequest() 222 o = j.template() 223 o['method'] = 'eth_call' 224 enc = ABIContractEncoder() 225 enc.method('executedTimes') 226 data = add_0x(enc.get()) 227 tx = c.template(self.accounts[0], self.address) 228 tx = c.set_code(tx, data) 229 o['params'].append(c.normalize(tx)) 230 o['params'].append('latest') 231 o = j.finalize(o) 232 r = self.rpc.do(o) 233 r = strip_0x(r) 234 self.assertEqual(int(r, 16), 0) 235 236 nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) 237 c = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) 238 enc = ABIContractEncoder() 239 enc.method('executeOfflineRequest') 240 enc.typ(ABIContractType.BYTES) 241 enc.typ(ABIContractType.BYTES) 242 enc.typ(ABIContractType.UINT8) 243 enc.bytes(msg_data.hex()) 244 enc.bytes(sig.hex()) 245 enc.uintn(0, 8) 246 data = enc.get() 247 tx = c.template(self.accounts[0], self.address, use_nonce=True) 248 tx = c.set_code(tx, data) 249 (tx_hash, o) = c.finalize(tx, TxFormat.JSONRPC) 250 self.rpc.do(o) 251 o = receipt(tx_hash) 252 r = self.rpc.do(o) 253 self.assertEqual(r['status'], 1) 254 255 c = TxFactory(self.chain_spec) 256 j = JSONRPCRequest() 257 o = j.template() 258 o['method'] = 'eth_call' 259 enc = ABIContractEncoder() 260 enc.method('executedTimes') 261 data = add_0x(enc.get()) 262 tx = c.template(self.accounts[0], self.address) 263 tx = c.set_code(tx, data) 264 o['params'].append(c.normalize(tx)) 265 o['params'].append('latest') 266 o = j.finalize(o) 267 r = self.rpc.do(o) 268 r = strip_0x(r) 269 self.assertEqual(int(r, 16), 1) 270 271 272 # TODO: check if uint96 etc works with packed data 273 def test_from_struct(self): 274 instruction_nonce = os.urandom(16) 275 enc_instruction = ABIContractEncoder() 276 # bytes12 does not work with this pyevm in struct encoding 277 #enc_instruction.typ(ABIContractType.BYTES32) 278 enc_instruction.typ(ABIContractType.BYTES32) 279 enc_instruction.typ(ABIContractType.UINT256) 280 enc_instruction.typ(ABIContractType.ADDRESS) 281 #enc_instruction.typ_literal('uint96') 282 #enc_instruction.bytesn(instruction_nonce.hex(), 12) 283 #enc_instruction.bytes32(hash_of_bar) 284 enc_instruction.bytes32(hash_of_foo) 285 enc_instruction.uint256(42) 286 enc_instruction.address(self.accounts[2]) 287 #enc_instruction.uintn(int(instruction_nonce.hex(), 16), 96) 288 289 c = TxFactory(self.chain_spec) 290 j = JSONRPCRequest() 291 o = j.template() 292 o['method'] = 'eth_call' 293 enc = ABIContractEncoder() 294 enc.method('toBytes') 295 enc.typ(enc_instruction) 296 enc.tuple(enc_instruction) 297 data = strip_0x(enc.get()) 298 data = data[:8] + data[8+64:] 299 data = add_0x(data) 300 tx = c.template(self.accounts[0], self.address) 301 tx = c.set_code(tx, data) 302 o['params'].append(c.normalize(tx)) 303 o['params'].append('latest') 304 o = j.finalize(o) 305 r = self.rpc.do(o) 306 r = strip_0x(r) 307 print(r) 308 309 310 def test_execute_erc712(self): 311 c = TxFactory(self.chain_spec) 312 j = JSONRPCRequest() 313 o = j.template() 314 o['method'] = 'eth_call' 315 enc = ABIContractEncoder() 316 enc.method('eip712DomainSeparator') 317 data = add_0x(enc.get()) 318 tx = c.template(self.accounts[0], self.address) 319 tx = c.set_code(tx, data) 320 o['params'].append(c.normalize(tx)) 321 o['params'].append('latest') 322 o = j.finalize(o) 323 r = self.rpc.do(o) 324 r = strip_0x(r) 325 326 domain = EIP712Domain( 327 name='Offline signing test', 328 version='0', 329 chain_id=self.chain_spec.chain_id(), 330 verifying_contract=self.address, 331 #salt=self.contract_block_hash, 332 salt='0x00', 333 ) 334 335 self.assertEqual(r, domain.get_contents().hex()) 336 337 c = TxFactory(self.chain_spec) 338 j = JSONRPCRequest() 339 o = j.template() 340 o['method'] = 'eth_call' 341 enc = ABIContractEncoder() 342 enc.method('eip712TypeHash') 343 data = add_0x(enc.get()) 344 tx = c.template(self.accounts[0], self.address) 345 tx = c.set_code(tx, data) 346 o['params'].append(c.normalize(tx)) 347 o['params'].append('latest') 348 o = j.finalize(o) 349 r = self.rpc.do(o) 350 r = strip_0x(r) 351 352 subdomain = os.urandom(32) 353 enc = EIP712DomainEncoder('Instruction', domain) 354 enc.add('domain', ABIContractType.BYTES32, subdomain) 355 enc.add('value', ABIContractType.UINT256, 42) 356 enc.add('beneficiary', ABIContractType.ADDRESS, self.accounts[2]) 357 358 self.assertEqual(r, enc.get_type_hash().hex()) 359 360 msg_data = enc.get_typed_data() 361 sig = self.signer.sign_typed_message(self.accounts[0], enc.get_domain(), enc.get_hash()) 362 sig = sig[:64] + (sig[64] + 27).to_bytes(1, byteorder='big') 363 logg.debug('message is:\n{}\nsigned by {}'.format(enc, self.accounts[0])) 364 365 nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) 366 c = TxFactory(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) 367 enc = ABIContractEncoder() 368 enc.method('executeOfflineRequest') 369 enc.typ(ABIContractType.BYTES) 370 enc.typ(ABIContractType.BYTES) 371 enc.typ(ABIContractType.UINT8) 372 enc.bytes(msg_data.hex()) 373 enc.bytes(sig.hex()) 374 enc.uintn(1, 8) 375 data = enc.get() 376 tx = c.template(self.accounts[0], self.address, use_nonce=True) 377 tx = c.set_code(tx, data) 378 (tx_hash, o) = c.finalize(tx, TxFormat.JSONRPC) 379 self.rpc.do(o) 380 o = receipt(tx_hash) 381 r = self.rpc.do(o) 382 self.assertEqual(r['status'], 1) 383 384 385 if __name__ == '__main__': 386 unittest.main()