def _decode_keystore_json(jsondata, password): # Get key derivation function (kdf) and parameters kdfparams = jsondata["crypto"]["kdfparams"] # Compute derived key derivedkey = pbkdf2.PBKDF2(password, decode_hex(kdfparams["salt"]), kdfparams["c"], SHA256).read(kdfparams["dklen"]) assert len( derivedkey) >= 32, "Derived key must be at least 32 bytes long" # Get cipher and parameters and decrypt using AES cipherparams = jsondata["crypto"]["cipherparams"] enckey = derivedkey[:16] iv = int.from_bytes(decode_hex(cipherparams["iv"]), byteorder="big") ctr = Counter.new(128, initial_value=iv, allow_wraparound=True) encryptor = AES.new(enckey, AES.MODE_CTR, counter=ctr) ctext = decode_hex(jsondata["crypto"]["ciphertext"]) o = encryptor.decrypt(ctext) # Compare the provided MAC with a locally computed MAC mac1 = sha3(derivedkey[16:32] + ctext) mac2 = decode_hex(jsondata["crypto"]["mac"]) if mac1 != mac2: raise ValueError("MAC mismatch. Password incorrect?") return o
def compute_state_test_unit(state, txdata, indices, konfig): state.env.config = konfig s = state.snapshot() try: # Create the transaction tx = transactions.Transaction( nonce=parse_int_or_hex(txdata['nonce'] or b"0"), gasprice=parse_int_or_hex(txdata['gasPrice'] or b"0"), startgas=parse_int_or_hex(txdata['gasLimit'][indices["gas"]] or b"0"), to=decode_hex(remove_0x_head(txdata['to'])), value=parse_int_or_hex(txdata['value'][indices["value"]] or b"0"), data=decode_hex(remove_0x_head(txdata['data'][indices["data"]]))) if 'secretKey' in txdata: tx.sign(decode_hex(remove_0x_head(txdata['secretKey']))) else: tx.v = parse_int_or_hex(txdata['v']) # Run it prev = state.to_dict() success, output = apply_transaction(state, tx) print("Applied tx") except InvalidTransaction as e: print("Exception: %r" % e) success, output = False, b'' # state.set_code('0x3e180b1862f9d158abb5e519a6d8605540c23682', b'') state.commit() post = state.to_dict() # print('pozt', post) output_decl = { "hash": '0x' + encode_hex(state.trie.root_hash), "indexes": indices, "diff": mk_state_diff(prev, post) } state.revert(s) return output_decl
def compute_state_test_unit(state, txdata, indices, konfig, is_qkc_state, qkc_env=None): state.env.config = konfig s = state.snapshot() if "transferTokenId" in txdata: transfer_token_id = parse_int_or_hex( txdata["transferTokenId"][indices["transferTokenId"]]) else: transfer_token_id = token_id_encode("QKC") try: # Create the transaction tx = transactions.Transaction( nonce=parse_int_or_hex(txdata["nonce"] or b"0"), gasprice=parse_int_or_hex(txdata["gasPrice"] or b"0"), startgas=parse_int_or_hex(txdata["gasLimit"][indices["gas"]] or b"0"), to=decode_hex(remove_0x_head(txdata["to"])), value=parse_int_or_hex(txdata["value"][indices["value"]] or b"0"), data=decode_hex(remove_0x_head(txdata["data"][indices["data"]])), gas_token_id=token_id_encode("QKC"), transfer_token_id=transfer_token_id, # Should not set testing flag if testing QuarkChain state is_testing=not is_qkc_state, ) tx.set_quark_chain_config(qkc_env.quark_chain_config) if "secretKey" in txdata: tx.sign(decode_hex(remove_0x_head(txdata["secretKey"]))) else: tx._in_mutable_context = True tx.v = parse_int_or_hex(txdata["v"]) tx._in_mutable_context = False # Run it prev = state.to_dict() success, output = apply_transaction(state, tx, tx_wrapper_hash=bytes(32)) except InvalidTransaction as e: print("Exception: %r" % e) success, output = False, b"" # touch coinbase, make behavior consistent with go-ethereum state.delta_token_balance(state.block_coinbase, token_id_encode("QKC"), 0) state.commit() post = state.to_dict() output_decl = { "hash": "0x" + encode_hex(state.trie.root_hash), "indexes": indices, "diff": mk_state_diff(prev, post), } state.revert(s) return output_decl
def coinbase(self): """Return the address that should be used as coinbase for new blocks. The coinbase address is given by the config field pow.coinbase_hex. If this does not exist or is `None`, the address of the first account is used instead. If there are no accounts, the coinbase is `DEFAULT_COINBASE`. :raises: :exc:`ValueError` if the coinbase is invalid (no string, wrong length) or there is no account for it and the config flag `accounts.check_coinbase` is set (does not apply to the default coinbase) """ cb_hex = self.app.config.get('pow', {}).get('coinbase_hex') if cb_hex is None: if not self.accounts_with_address: return DEFAULT_COINBASE cb = self.accounts_with_address[0].address else: # [NOTE]: check it! # if not is_string(cb_hex): if not isinstance(cb_hex, str): raise ValueError('coinbase must be string') try: cb = decode_hex(remove_0x_head(cb_hex)) except (ValueError, TypeError): raise ValueError('invalid coinbase') if len(cb) != 20: raise ValueError('wrong coinbase length') if self.config['accounts']['must_include_coinbase']: if cb not in (acct.address for acct in self.accounts): raise ValueError('no account for coinbase') return cb
def _make_keystore_json(self, password): """ Generate the keystore json that follows the Version 3 specification: https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition#definition Uses pbkdf2 for password encryption, and AES-128-CTR as the cipher. """ # Get the hash function and default parameters kdfparams = { "prf": "hmac-sha256", "dklen": 32, "c": 262144, "salt": encode_hex(os.urandom(16)), } # Compute derived key derivedkey = pbkdf2.PBKDF2(password, decode_hex(kdfparams["salt"]), kdfparams["c"], SHA256).read(kdfparams["dklen"]) # Produce the encryption key and encrypt using AES enckey = derivedkey[:16] cipherparams = {"iv": encode_hex(os.urandom(16))} iv = int.from_bytes(decode_hex(cipherparams["iv"]), byteorder="big") ctr = Counter.new(128, initial_value=iv, allow_wraparound=True) encryptor = AES.new(enckey, AES.MODE_CTR, counter=ctr) c = encryptor.encrypt(self.identity.key) # Compute the MAC mac = sha3(derivedkey[16:32] + c) # Return the keystore json return { "crypto": { "cipher": "aes-128-ctr", "ciphertext": encode_hex(c), "cipherparams": cipherparams, "kdf": "pbkdf2", "kdfparams": kdfparams, "mac": encode_hex(mac), "version": 1, }, "id": self.uuid, "version": 3, }
def from_snapshot(cls, snapshot_data, env, executing_on_head=False): state = State(env=env) if "alloc" in snapshot_data: for addr, data in snapshot_data["alloc"].items(): if len(addr) == 40: addr = decode_hex(addr) assert len(addr) == 20 if "wei" in data: state.set_balance(addr, parse_as_int(data["wei"])) if "balance" in data: state.set_balance(addr, parse_as_int(data["balance"])) if "code" in data: state.set_code(addr, parse_as_bin(data["code"])) if "nonce" in data: state.set_nonce(addr, parse_as_int(data["nonce"])) if "storage" in data: for k, v in data["storage"].items(): state.set_storage_data( addr, big_endian_to_int(parse_as_bin(k)), big_endian_to_int(parse_as_bin(v)), ) elif "state_root" in snapshot_data: state.trie.root_hash = parse_as_bin(snapshot_data["state_root"]) else: raise Exception( "Must specify either alloc or state root parameter") for k, default in STATE_DEFAULTS.items(): default = copy.copy(default) v = snapshot_data[k] if k in snapshot_data else None if is_numeric(default): setattr(state, k, parse_as_int(v) if k in snapshot_data else default) elif is_string(default): setattr(state, k, parse_as_bin(v) if k in snapshot_data else default) elif k == "prev_headers": if k in snapshot_data: headers = [dict_to_prev_header(h) for h in v] else: headers = default setattr(state, k, headers) elif k == "recent_uncles": if k in snapshot_data: uncles = {} for height, _uncles in v.items(): uncles[int(height)] = [] for uncle in _uncles: uncles[int(height)].append(parse_as_bin(uncle)) else: uncles = default setattr(state, k, uncles) if executing_on_head: state.executing_on_head = True state.commit() state.changed = {} return state
def test_proc_transfer_mnt(self): sender = b"\x00" * 19 + b"\x34" precompiled = decode_hex(b"000000000000000000000000000000514b430002") token_id = 1234567 token_id_bytes = token_id.to_bytes(32, byteorder="big") state = State() self.__mint( state, sender, token_id_encode("QKC").to_bytes(32, byteorder="big"), encode_int32(30000), ) self.__mint(state, sender, token_id_bytes, encode_int32(100)) balance = state.get_balance(sender, token_id) self.assertEqual(balance, 100) balance = state.get_balance(sender, token_id_encode("QKC")) self.assertEqual(balance, 30000) new_addr = b"\x01" * 20 testcases = [ # Format: (index, description, to, value, gas, expected ret, post check) (1, "no value transfer", sender, 0, 0, (1, 0, []), None), (2, "value transfer needs more gas", sender, 1, 8999, (0, 0, []), None), # Should have stipend gas left (3, "value transfer needs more gas", sender, 1, 9000, (1, 2300, []), None), (4, "tx on new addr needs more gas", new_addr, 1, 33999, (0, 0, []), None), ( 5, "tx on new addr needs more gas", new_addr, 42, 34000, (1, 2300, []), lambda err_msg: self.assertEqual( state.get_balance(new_addr, token_id), 42, err_msg), ), (6, "insufficient balance", sender, 333, 9000, (0, 0, []), None), (7, "target address is special", precompiled, 0, 9000, (0, 0, []), None), ] for i, desc, addr, value, gas, expected_ret, post_check in testcases: # Data = to address + token ID ++ value ++ (optional) msg data data = (b"\x00" * 12 + addr + token_id_bytes + value.to_bytes(32, byteorder="big")) msg = Message(sender, sender, gas=gas, data=data) ret = proc_transfer_mnt(VMExt(state, sender, gas_price=1), msg) self.assertEqual(ret, expected_ret, "%d: %s" % (i, desc)) if callable(post_check): post_check("%d: %s" % (i, desc))
def find(self, identifier): """Find an account by either its address, its id or its index as string. Example identifiers: - '9c0e0240776cfbe6fa1eb37e57721e1a88a563d1' (address) - '0x9c0e0240776cfbe6fa1eb37e57721e1a88a563d1' (address with 0x prefix) - '01dd527b-f4a5-4b3c-9abb-6a8e7cd6722f' (UUID) - '3' (index) :param identifier: the accounts hex encoded, case insensitive address (with optional 0x prefix), its UUID or its index (as string, >= 1) in `account_service.accounts` :raises: :exc:`ValueError` if the identifier could not be interpreted :raises: :exc:`KeyError` if the identified account is not known to the account_service """ try: uuid = UUID(identifier) except ValueError: pass else: return self.get_by_id(uuid.hex) try: index = int(identifier, 10) except ValueError: pass else: if index <= 0: raise ValueError('Index must be 1 or greater') try: return self.accounts[index - 1] except IndexError as e: raise KeyError(e.message) if identifier[:2] == '0x': identifier = identifier[2:] try: address = decode_hex(identifier) except TypeError: success = False else: if len(address) != 20: success = False else: return self[address] assert not success raise ValueError('Could not interpret account identifier')
def init_state(env, pre, is_qkc_state, qkc_env=None): # Setup env db = InMemoryDb() state_env = Env(config=konfig) state_env.db = db state = State( env=state_env, block_prevhash=decode_hex(remove_0x_head(env["previousHash"])), prev_headers=[ mk_fake_header(i) for i in range( parse_int_or_hex(env["currentNumber"]) - 1, max(-1, parse_int_or_hex(env["currentNumber"]) - 257), -1, ) ], block_number=parse_int_or_hex(env["currentNumber"]), block_coinbase=decode_hex(remove_0x_head(env["currentCoinbase"])), block_difficulty=parse_int_or_hex(env["currentDifficulty"]), gas_limit=parse_int_or_hex(env["currentGasLimit"]), timestamp=parse_int_or_hex(env["currentTimestamp"]), qkc_config=qkc_env.quark_chain_config, # If testing QuarkChain states, should not use mock account use_mock_evm_account=not is_qkc_state, ) seen_token_ids = set() # Fill up pre for address, h in list(pre.items()): assert len(address) in (40, 42) address = decode_hex(remove_0x_head(address)) state.set_nonce(address, parse_int_or_hex(h["nonce"])) if is_qkc_state and "balances" in h: # In QuarkChain state tests, can either specify balance map or single balance for token_id, balance in h["balances"].items(): parsed_token_id = parse_int_or_hex(token_id) state.set_token_balance( address, parsed_token_id, parse_int_or_hex(balance) ) seen_token_ids.add(parsed_token_id) else: state.set_balance(address, parse_int_or_hex(h["balance"])) state.set_code(address, decode_hex(remove_0x_head(h["code"]))) for k, v in h["storage"].items(): state.set_storage_data( address, big_endian_to_int(decode_hex(k[2:])), big_endian_to_int(decode_hex(v[2:])), ) # Update allowed token IDs if seen_token_ids: state.qkc_config._allowed_token_ids = seen_token_ids state.commit(allow_empties=True) return state
def proc_transfer_mnt(ext, msg): from quarkchain.evm.messages import apply_msg # Data must be >= 96 bytes and static call not allowed if msg.data.size < 96 or msg.static: return 0, 0, [] to = utils.int_to_addr(msg.data.extract32(0)) token_id = msg.data.extract32(32) value = msg.data.extract32(64) data = msg.data.extract_all(96) # Token ID should be within range if token_id > TOKEN_ID_MAX: return 0, 0, [] # Doesn't allow target address to be this precompiled contract itself if to == decode_hex(b"000000000000000000000000000000514b430002"): return 0, 0, [] gascost = 0 if value > 0: gascost += opcodes.GCALLVALUETRANSFER if not ext.account_exists(to): gascost += opcodes.GCALLNEWACCOUNT # Out of gas if msg.gas < gascost: return 0, 0, [] # Handle insufficient balance or exceeding max call depth if ext.get_balance(msg.sender, token_id) < value or msg.depth >= vm.MAX_DEPTH: return 0, msg.gas - gascost, [] new_msg = vm.Message( msg.sender, to, value, msg.gas - gascost + opcodes.GSTIPEND * (value > 0), data, msg.depth + 1, code_address=to, static=False, transfer_token_id=token_id, gas_token_id=msg.gas_token_id, ) return apply_msg(ext, new_msg)
def test_proc_mint_mnt(self): sys_contract_addr = decode_hex( b"514b430000000000000000000000000000000002") random_addr = Address.create_random_account().recipient testcases = [(sys_contract_addr, True), (random_addr, False)] minter = b"\x00" * 19 + b"\x34" token_id = b"\x00" * 28 + b"\x11" * 4 amount = b"\x00" * 30 + b"\x22" * 2 data = b"\x00" * 12 + minter + token_id + amount for addr, expect_success in testcases: msg = Message(addr, addr, gas=34001, data=data) state = State() result, gas_remained, ret = proc_mint_mnt(VmExtBase(state), msg) balance = state.get_balance( minter, int.from_bytes(token_id, byteorder="big")) if expect_success: self.assertListEqual([result, gas_remained], [1, 34001 - 34000]) self.assertEqual(len(ret), 32) self.assertEqual(int.from_bytes(ret, byteorder="big"), 1) self.assertEqual(balance, int.from_bytes(amount, byteorder="big")) # Mint again with exactly the same parameters result, gas_remained, ret = proc_mint_mnt( VmExtBase(state), msg) balance = state.get_balance( minter, int.from_bytes(token_id, byteorder="big")) self.assertListEqual([result, gas_remained], [1, 34001 - 9000]) self.assertEqual(len(ret), 32) self.assertEqual(int.from_bytes(ret, byteorder="big"), 1) self.assertEqual(balance, 2 * int.from_bytes(amount, byteorder="big")) else: self.assertListEqual([result, gas_remained], [0, 0]) self.assertEqual(len(ret), 0) self.assertEqual(balance, 0)
def init_state(env, pre): # Setup env db = InMemoryDb() stateEnv = Env(config=konfig) stateEnv.db = db state = State( env=stateEnv, block_prevhash=decode_hex(remove_0x_head(env["previousHash"])), prev_headers=[ mk_fake_header(i) for i in range( parse_int_or_hex(env["currentNumber"]) - 1, max(-1, parse_int_or_hex(env["currentNumber"]) - 257), -1, ) ], block_number=parse_int_or_hex(env["currentNumber"]), block_coinbase=decode_hex(remove_0x_head(env["currentCoinbase"])), block_difficulty=parse_int_or_hex(env["currentDifficulty"]), gas_limit=parse_int_or_hex(env["currentGasLimit"]), timestamp=parse_int_or_hex(env["currentTimestamp"]), ) # Fill up pre for address, h in list(pre.items()): assert len(address) in (40, 42) address = decode_hex(remove_0x_head(address)) assert set(h.keys()) == set(["code", "nonce", "balance", "storage"]) state.set_nonce(address, parse_int_or_hex(h["nonce"])) state.set_balance(address, parse_int_or_hex(h["balance"])) state.set_code(address, decode_hex(remove_0x_head(h["code"]))) for k, v in h["storage"].items(): state.set_storage_data( address, big_endian_to_int(decode_hex(k[2:])), big_endian_to_int(decode_hex(v[2:])), ) state.commit(allow_empties=True) # state.commit() return state
from py_ecc.secp256k1 import N as secp256k1n import hashlib from quarkchain.constants import ( ROOT_CHAIN_POSW_CONTRACT_BYTECODE, NON_RESERVED_NATIVE_TOKEN_CONTRACT_BYTECODE, GENERAL_NATIVE_TOKEN_CONTRACT_BYTECODE, ) from quarkchain.rlp.utils import ascii_chr from quarkchain.evm import utils, opcodes, vm from quarkchain.evm.utils import safe_ord, decode_hex, encode_int32 from quarkchain.utils import check, TOKEN_ID_MAX ZERO_PRIVKEY_ADDR = decode_hex("3f17f1962b36e491b30a40b2405849e597ba5fb5") def proc_ecrecover(ext, msg): OP_GAS = opcodes.GECRECOVER gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] message_hash_bytes = [0] * 32 msg.data.extract_copy(message_hash_bytes, 0, 0, 32) message_hash = b"".join(map(ascii_chr, message_hash_bytes)) # TODO: This conversion isn't really necessary. # TODO: Investigate if the check below is really needed. v = msg.data.extract32(32)
# -*- coding: utf8 -*- from py_ecc.secp256k1 import privtopub, ecdsa_raw_recover, N as secp256k1n import hashlib from rlp.utils import ascii_chr from quarkchain.evm import utils, opcodes from quarkchain.evm.utils import safe_ord, decode_hex, encode_int32 ZERO_PRIVKEY_ADDR = decode_hex('3f17f1962b36e491b30a40b2405849e597ba5fb5') def proc_ecrecover(ext, msg): # print('ecrecover proc', msg.gas) OP_GAS = opcodes.GECRECOVER gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] message_hash_bytes = [0] * 32 msg.data.extract_copy(message_hash_bytes, 0, 0, 32) message_hash = b''.join(map(ascii_chr, message_hash_bytes)) # TODO: This conversion isn't really necessary. # TODO: Invesitage if the check below is really needed. v = msg.data.extract32(32) r = msg.data.extract32(64) s = msg.data.extract32(96) if r >= secp256k1n or s >= secp256k1n or v < 27 or v > 28: return 1, msg.gas - opcodes.GECRECOVER, [] try:
def __mint(state, minter, token, amount): sys_contract_addr = decode_hex( b"514b430000000000000000000000000000000002") data = b"\x00" * 12 + minter + token + amount msg = Message(sys_contract_addr, bytes(20), gas=34000, data=data) return proc_mint_mnt(VmExtBase(state), msg)
def create_minor_block( self, root_block: RootBlock, full_shard_id: int, evm_state: EvmState) -> Tuple[MinorBlock, TokenBalanceMap]: """ Create genesis block for shard. Genesis block's hash_prev_root_block is set to the genesis root block. Genesis state will be committed to the given evm_state. Based on ALLOC, genesis_token will be added to initial accounts. """ branch = Branch(full_shard_id) shard_config = self._qkc_config.shards[full_shard_id] genesis = shard_config.GENESIS for address_hex, alloc_data in genesis.ALLOC.items(): address = Address.create_from(bytes.fromhex(address_hex)) check( self._qkc_config.get_full_shard_id_by_full_shard_key( address.full_shard_key) == full_shard_id) evm_state.full_shard_key = address.full_shard_key recipient = address.recipient if "code" in alloc_data: code = decode_hex(remove_0x_head(alloc_data["code"])) evm_state.set_code(recipient, code) evm_state.set_nonce(recipient, 1) if "storage" in alloc_data: for k, v in alloc_data["storage"].items(): evm_state.set_storage_data( recipient, big_endian_to_int(decode_hex(k[2:])), big_endian_to_int(decode_hex(v[2:])), ) # backward compatible: # v1: {addr: {QKC: 1234}} # v2: {addr: {balances: {QKC: 1234}, code: 0x, storage: {0x12: 0x34}}} balances = alloc_data if "balances" in alloc_data: balances = alloc_data["balances"] for k, v in balances.items(): if k in ("code", "storage"): continue evm_state.delta_token_balance(recipient, token_id_encode(k), v) evm_state.commit() meta = MinorBlockMeta( hash_merkle_root=bytes.fromhex(genesis.HASH_MERKLE_ROOT), hash_evm_state_root=evm_state.trie.root_hash, xshard_tx_cursor_info=XshardTxCursorInfo(root_block.header.height, 0, 0), ) local_fee_rate = 1 - self._qkc_config.reward_tax_rate # type: Fraction coinbase_tokens = { self._qkc_config.genesis_token: shard_config.COINBASE_AMOUNT * local_fee_rate.numerator // local_fee_rate.denominator } coinbase_address = Address.create_empty_account(full_shard_id) header = MinorBlockHeader( version=genesis.VERSION, height=genesis.HEIGHT, branch=branch, hash_prev_minor_block=bytes.fromhex(genesis.HASH_PREV_MINOR_BLOCK), hash_prev_root_block=root_block.header.get_hash(), evm_gas_limit=genesis.GAS_LIMIT, hash_meta=sha3_256(meta.serialize()), coinbase_amount_map=TokenBalanceMap(coinbase_tokens), coinbase_address=coinbase_address, create_time=genesis.TIMESTAMP, difficulty=genesis.DIFFICULTY, extra_data=bytes.fromhex(genesis.EXTRA_DATA), ) return ( MinorBlock(header=header, meta=meta, tx_list=[]), TokenBalanceMap(coinbase_tokens), )
import os import pbkdf2 from random import SystemRandom import shutil from uuid import UUID, uuid4 from Crypto.Cipher import ( AES, ) # Crypto imports necessary for AES encryption of the private key from Crypto.Hash import SHA256 from Crypto.Util import Counter from quarkchain.core import Address, Identity from quarkchain.evm.slogging import get_logger from quarkchain.evm.utils import decode_hex, encode_hex, is_string, sha3, to_string log = get_logger("accounts") DEFAULT_COINBASE = decode_hex("de0b295669a9fd93d5f28d9ec85e40f4cb697bae") random = SystemRandom() @total_ordering class MinType(object): """ Return Min value for sorting comparison This class is used for comparing unorderded types. e.g., NoneType """ def __le__(self, other): return True def __eq__(self, other): return self is other