class UnsignedChainId(HashableRLP): fields = ( ('directive', big_endian_int), ( 'stakeMsg', List( [ # list with the following members Binary.fixed_length( 20, allow_empty=True), # validatorAddress List([Text()] * 5, True), # description is Text of 5 elements List([big_endian_int], True), # new rate is in a list big_endian_int, # min self delegation big_endian_int, # max total delegation Binary.fixed_length( 48, allow_empty=True), # slot key to remove Binary.fixed_length( 48, allow_empty=True), # slot key to add ], True)), # strictly these number of elements ('nonce', big_endian_int), ('gasPrice', big_endian_int), ('gasLimit', big_endian_int), ('chainId', big_endian_int), )
class UnsignedChainId(HashableRLP): fields = ( ('directive', big_endian_int), ( 'stakeMsg', List( [ # list with the following members Binary.fixed_length( 20, allow_empty=True), # validatorAddress List([Text()] * 5, True), # description is Text of 5 elements List( [List([big_endian_int], True)] * 3, True ), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] big_endian_int, # min self delegation big_endian_int, # max total delegation CountableList( Binary.fixed_length(48, allow_empty=True) ), # bls-public-keys array of unspecified length, each key of 48 big_endian_int, # amount ], True)), # strictly these number of elements ('nonce', big_endian_int), ('gasPrice', big_endian_int), ('gasLimit', big_endian_int), ('chainId', big_endian_int), )
def test_list_sedes(): l1 = List() l2 = List((big_endian_int, big_endian_int)) l3 = List((l1, l2, [[[]]])) l1.serialize([]) l2.serialize((2, 3)) l3.serialize([[], [5, 6], [[[]]]]) with pytest.raises(SerializationError): l1.serialize([[]]) with pytest.raises(SerializationError): l1.serialize([5]) for d in ([], [1, 2, 3], [1, [2, 3], 4]): with pytest.raises(SerializationError): l2.serialize(d) for d in ([], [[], [], [[[]]]], [[], [5, 6], [[]]]): with pytest.raises(SerializationError): l3.serialize(d) c = CountableList(big_endian_int) assert l1.deserialize(c.serialize([])) == () for s in (c.serialize(l) for l in [[1], [1, 2, 3], range(30), (4, 3)]): with pytest.raises(DeserializationError): l1.deserialize(s) valid = [(1, 2), (3, 4), (9, 8)] for s, v in ((c.serialize(v), v) for v in valid): assert l2.deserialize(s) == v invalid = [[], [1], [1, 2, 3]] for s in (c.serialize(i) for i in invalid): with pytest.raises(DeserializationError): l2.deserialize(s)
def serialize(cls, obj): sedes_list = List([ big_endian_int, List([ Binary.fixed_length(20), Binary.fixed_length(20), big_endian_int ]) ]) tx_elems = [obj.output_type, [obj.output_guard, obj.token, obj.amount]] tx_sedes = rlp.sedes.List(sedes_list) return tx_sedes.serialize(tx_elems)
class Body(rlp.Serializable): fields = ( ('Source', text), ('Fee', text), ('SequenceID', big_endian_int), ('Operations', List((Operation, ), False)), ) def serialize(self): cl = list() for i in self.as_dict()['Operations']: cl.append(i.__class__) class t(rlp.Serializable): fields = ( ('Source', text), ('Fee', text), ('SequenceID', big_endian_int), ('Operations', List(tuple(cl), False)), ) d = self.as_dict() tt = t( Source=d['Source'], Fee=d['Fee'], SequenceID=d['SequenceID'], Operations=d['Operations'], ) return tt.serialize(tt)
class t(rlp.Serializable): fields = ( ('Source', text), ('Fee', text), ('SequenceID', big_endian_int), ('Operations', List(tuple(cl), False)), )
def test_list_of_serializable_decoding_rlp_caching(rlp_obj): rlp_obj_code = encode(rlp_obj, cache=False) L = [rlp_obj, rlp_obj] list_code = encode(L, cache=False) L2 = decode(list_code, sedes=List((type(rlp_obj), type(rlp_obj))), recursive_cache=True) assert L2[0]._cached_rlp == rlp_obj_code assert L2[1]._cached_rlp == rlp_obj_code
def _get_serializer(cls): if cls._serializer is not None: return cls._serializer serializers = [serializer for _, serializer in cls.fields] serializer = serializers[0] if len(serializers) == 1 else List( serializers) cls._serializer = serializer return serializer
def test_list_sedes(): l1 = List() l2 = List((big_endian_int, big_endian_int)) l3 = List((l1, l2, [[[]]])) l1.serialize([]) l2.serialize((2, 3)) l3.serialize([[], [5, 6], [[[]]]]) with pytest.raises(SerializationError): l1.serialize([[]]) with pytest.raises(SerializationError): l1.serialize([5]) for d in ([], [1, 2, 3], [1, [2, 3], 4]): with pytest.raises(SerializationError): l2.serialize(d) for d in ([], [[], [], [[[]]]], [[], [5, 6], [[]]]): with pytest.raises(SerializationError): l3.serialize(d)
def test_inference(): obj_sedes_pairs = ( (5, big_endian_int), (0, big_endian_int), (-1, None), (b'', binary), (b'asdf', binary), (b'\xe4\xf6\xfc\xea\xe2\xfb', binary), ([], List()), ([1, 2, 3], List((big_endian_int, ) * 3)), ([[], b'asdf'], List(([], binary))), ) for obj, sedes in obj_sedes_pairs: if sedes is not None: inferred = infer_sedes(obj) assert inferred == sedes sedes.serialize(obj) else: with pytest.raises(TypeError): infer_sedes(obj)
def header_event_watcher(log): header_logs, add_header_topic # print the last log and store the recent received one if log.topics[0] == add_header_topic: # print(log.data) header_logs.append(log.data) if len(header_logs) > 1: last_log = header_logs.pop(0) # [num, num, bytes32, bytes32, bytes32, address, bytes32, bytes32, bytes] # use sedes to prevent integer 0 from being decoded as b'' sedes = List([utils.big_endian_int, utils.big_endian_int, utils.hash32, utils.hash32, utils.hash32, utils.address, utils.hash32, utils.hash32, binary]) values = rlp.decode(last_log, sedes) print("add_header: shard_id={}, expected_period_number={}, header_hash={}, parent_header_hash={}".format(values[0], values[1], utils.sha3(last_log), values[3]))
class UnsignedChainId(HashableRLP): fields = ( ('directive', big_endian_int), ('stakeMsg', List([ Binary.fixed_length(20, allow_empty=True), Binary.fixed_length(20, allow_empty=True), big_endian_int ], True)), ('nonce', big_endian_int), ('gasPrice', big_endian_int), ('gasLimit', big_endian_int), ('chainId', big_endian_int), )
def sign_apphash(apphash): sedes = CountableList(List([rlp.sedes.binary, rlp.sedes.big_endian_int, rlp.sedes.big_endian_int, rlp.sedes.binary, rlp.sedes.binary])) decoded = rlp.decode(bytes.fromhex(apphash), sedes) for i in decoded: data = list(i) data[2] = bool(data[2]) msg_hash = w3.soliditySha3(['bytes32', 'uint256', 'bool', 'bytes', 'bytes'], data).hex()[2:] signed = utils.ecsign(bytes.fromhex(msg_hash), operator_normalize_key) tx = '1{0:0{1}X}{2:0{3}X}{4:0{5}X}{6:X}'.format(signed[1], 64, signed[2], 64, signed[0], 2, data[1]) requests.get('http://localhost:26657/broadcast_tx_async?tx="{}"'.format(tx))
def mine(self, number_of_blocks=1, coinbase=a0): self.cs.finalize(self.head_state, self.block) set_execution_results(self.head_state, self.block) self.block = Miner(self.block).mine(rounds=100, start_nonce=0) assert self.chain.add_block(self.block) b = self.block # Reorganize head collation collation = None # Check add_header_logs for item in self.add_header_logs: # [num, num, bytes32, bytes32, bytes32, address, bytes32, bytes32, bytes] # use sedes to prevent integer 0 from being decoded as b'' sedes = List([ utils.big_endian_int, utils.big_endian_int, utils.hash32, utils.hash32, utils.hash32, utils.address, utils.hash32, utils.hash32, binary ]) values = rlp.decode(item, sedes) shard_id = values[0] if shard_id in self.chain.shard_id_list: collation_hash = sha3(item) collation = self.chain.shards[shard_id].get_collation( collation_hash) self.chain.reorganize_head_collation(b, collation) # Clear logs self.add_header_logs = [] for i in range(1, number_of_blocks): b, _ = make_head_candidate(self.chain, parent=b, timestamp=self.chain.state.timestamp + 14, coinbase=coinbase) b = Miner(b).mine(rounds=100, start_nonce=0) assert self.chain.add_block(b) self.chain.reorganize_head_collation(b, None) self.change_head(b.header.hash, coinbase) return b
def decode_tx(tx): if tx.startswith('0x'): tx = tx[2:len(tx)] sedes = List([ big_endian_int, big_endian_int, big_endian_int, binary, big_endian_int, binary, big_endian_int, binary, binary ]) txc = codecs.decode(tx, "hex") keys = [ 'nonce', 'gasPrice', 'gasLimit', 'address', 'value', 'data', 'v', 'r', 's' ] values = rlp.decode(txc, sedes) tx = {} for k, v in zip(keys, values): if k in ['address', 'r', 's']: tx[k] = binascii.hexlify(v).decode('ascii') elif k in ['data']: tx[k] = v.decode('ascii') else: tx[k] = v return tx
def L_S(t): #(Tn, Tp, Tg, Tt, Tv, p) if v ∈ {27, 28} fs = [ int(j["nounce"]), int(j["gasPrice"]), int(j["gasLimit"]), bytes.fromhex(j["to"]), int(j["value"]), bytes.fromhex(j["data"]) ] list_sedes = List([ big_endian_int, big_endian_int, big_endian_int, binary, big_endian_int, binary ]) if "w" in j: fs += [int(j["w"]), int(j["r"]), int(j["s"])] list_sedes += [big_endian_int, big_endian_int, big_endian_int] else: fs += [int(j["chain_id"]), bytes.fromhex(""), bytes.fromhex("")] list_sedes += [big_endian_int, binary, binary] return rlp.encode(fs, list_sedes)
class NewKey(BaseMessage): content_sedes = List([ big_endian_int, # sender index big_endian_int, # timestamp big_endian_int, # extra with sender type CountableList(raw), # [k(j, i)] ]) payload_sedes = List([ raw, # content raw, # auth(signature) ]) def __init__(self, sender:int, reqid:Reqid, extra, hmac_keys): """Session key """ super().__init__() self.sender = sender self.reqid = reqid self.extra = extra self.hmac_keys = hmac_keys self.content = None self.auth = None self.payload = None self.from_addr = None def verify(self, peer_principal): return peer_principal.verify(self.content_digest, self.auth) def __str__(self): return '{}:{}\n{}\n{}\n{}'.format(self.sender, self.reqid, self.extra, self.hmac_keys, self.auth) @property def content_digest(self): d = hashlib.sha256() d.update('{}'.format(self.sender).encode()) d.update('{}'.format(self.reqid).encode()) d.update('{}'.format(self.extra).encode()) for k in self.hmac_keys: d.update(k) return d.digest() @classmethod def from_node(cls, node): hmac_keys = [] for p in node.replica_principals: if p is node.principal: nonce = p.zero_hmac_nounce else: nonce = p.gen_inkey() hmac_keys.append(p.encrypt(nonce)) extra = 0 if node.type is 'Client': extra |= 1 << 4 message = cls(node.sender, node.next_reqid(), extra, hmac_keys) message.content = rlp.encode([message.sender, message.reqid, message.extra, message.hmac_keys], cls.content_sedes) message.auth = node.principal.sign(message.content_digest) message.payload = rlp.encode([message.content, message.auth], cls.payload_sedes) return message @classmethod def from_payload(cls, payload, addr, _node): try: [content, auth] = rlp.decode(payload, cls.payload_sedes) [sender, reqid, extra, hmac_keys] = ( rlp.decode(content, cls.content_sedes)) message = cls(sender, reqid, extra, hmac_keys) message.content = content message.auth = auth message.payload = payload message.from_addr = addr return message except rlp.DecodingError as exc: raise ValueError('decoding error: {}'.format(exc))
class Prepare(): content_sedes = List([ big_endian_int, # view big_endian_int, # seqno big_endian_int, # extra big_endian_int, # sender raw, # consensus_digest of pre_prepare ]) payload_sedes = List([ raw, # content raw, # auth(signature) ]) def __init__(self, view, seqno, extra, sender, consensus_digest): self.view = view self.seqno = seqno self.extra = extra self.sender = sender self.consensus_digest = consensus_digest self.content = None self.auth = None self.payload = None @property def use_signature(self): return True if self.extra & 2 else False @use_signature.setter def use_signature(self, val): if val: self.extra |= 2 else: self.extra &= ~2 @classmethod def from_backup(cls, backup, view, seqno, use_signature:bool, consensus_digest): extra = 0 if use_signature: extra |= 2 return cls(view, seqno, extra, backup.index, consensus_digest) @classmethod def from_payload(cls, payload, addr, _node): try: [content, auth] = rlp.decode(payload, cls.payload_sedes) [view, seqno, extra, sender, consensus_digest] = rlp.decode( content, cls.content_sedes) message = cls(view, seqno, extra, sender, consensus_digest) message.content = content message.auth = auth message.payload = payload return message except rlp.DecodingError as exc: raise ValueError('decoding error: {}'.format(exc))
j = json.loads(ret.text) return j if __name__ == '__main__': block = {} while True: latest = tendermint_latest_block() for blk in range(last_block + 1, latest + 1): print('blk', blk) tdm_block = get_tendermint_block(blk) apphash = tdm_block['result']['block_meta']['header']['app_hash'] sedes = CountableList( List([ rlp.sedes.binary, rlp.sedes.big_endian_int, rlp.sedes.big_endian_int, rlp.sedes.binary, rlp.sedes.binary ])) d = rlp.decode(bytes.fromhex(apphash), sedes) for i in d: block[i[1]] = Block(i[0], i[1], bool(i[2]), i[3], i[4]) plasma_latest_block = i[1] txs = tdm_block['result']['block']['data']['txs'] for tx in txs: decoded_tx = base64.b64decode(tx).decode() if decoded_tx[0] == '1': decoded_tx = decoded_tx[1:] sig = decoded_tx[:130] blkNum = int(decoded_tx[130:], 16) block[blkNum].add_signature(sig) # block[i[1]].submit()
class Reply(BaseMessage): content_sedes = List([ big_endian_int, # view big_endian_int, # timestamp(reqid) big_endian_int, # extra big_endian_int, # requestor index big_endian_int, # replier index raw, # result or digest ]) payload_sedes = List([ raw, # content raw, # auth(hmac) ]) def __init__(self, view, reqid, extra, sender, replier, result: bytes): """ :sender index id of replica sending this message :reqid reqid from the sender :node_type always be replica """ super().__init__() self.view = view self.reqid = reqid self.extra = extra self.sender = sender # sender of request self.replier = replier self.result = result self.requestor = None # principal self.content = None self.auth = None self.payload = None @property def reply_digest(self): d = hashlib.sha256() d.update(self.reply) return d.digest() @property def content_digest(self): d = hashlib.sha256() d.update('{}'.format(self.view).encode()) d.update('{}'.format(self.reqid).encode()) d.update('{}'.format(self.extra).encode()) d.update('{}'.format(self.sender).encode()) d.update('{}'.format(self.replier).encode()) d.update('{}'.format(self.reply_digest).encode()) return d.digest() @classmethod def from_node(cls, node, request, reply): extra = 0 if node.type is 'Client': extra |= 1 << 4 Message = cls(node.index, request.reqid, extra, node.view, reply) return message @classmethod def from_payload(cls, payload, addr, _node): try: [content, auth] = rlp.decode(payload, cls.payload_sedes) [sender, reqid, extra, view, reply_digest, reply] = (rlp.decode(content, cls.content_sedes)) message = cls(sender, reqid, extra, view, reply) if message.reply_digest != reply_digest: raise ValueError('digest error') message.auth = auth message.from_addr = addr return message except rlp.DecodingErr as exc: raise ValueError('decoding error: {}'.format(exc))
import sys import argparse import hmac import qrcode import rlp import subprocess import numpy as np from tqdm import tqdm from PIL import Image from PIL import ImageFont from PIL import ImageDraw from rlp.sedes import big_endian_int, List from rlp.exceptions import ListDeserializationError from base64 import urlsafe_b64encode, urlsafe_b64decode short_header_fmt = List([big_endian_int, big_endian_int]) header_fmt = List([big_endian_int, big_endian_int, big_endian_int]) _FONT_PATH = os.getenv('FONT_PATH', 'Arial.ttf') def mkdirs_exists_ok(path): try: os.makedirs(path) except OSError: if not os.path.isdir(path): raise def get_arg_parser(): parser = argparse.ArgumentParser( formatter_class=argparse.ArgumentDefaultsHelpFormatter)
( (5, big_endian_int), (0, big_endian_int), (-1, None), (True, boolean), (False, boolean), (None, None), (b'', binary), (b'asdf', binary), (b'\xe4\xf6\xfc\xea\xe2\xfb', binary), ('', text), ('asdf', text), ('\xe4\xf6\xfc\xea\xe2\xfb', text), ('你好世界', text), ('\u4f60\u597d\u4e16\u754c', text), ([], List()), ([1, 2, 3], List((big_endian_int, ) * 3)), ([[], b'asdf'], List(([], binary))), ([1, 'asdf'], List((big_endian_int, text))), ), ) def test_inference(value, expected): if expected is not None: inferred = infer_sedes(value) assert inferred == expected expected.serialize(value) else: with pytest.raises(TypeError): infer_sedes(value)
class RLPType2(Serializable): fields = [ ('field2_1', RLPType1), ('field2_2', List((RLPType1, RLPType1))), ]
class RLPType1(Serializable): fields = [ ('field1', big_endian_int), ('field2', binary), ('field3', List((big_endian_int, binary))) ]
tuple(self.items()), )) def __repr__(self) -> str: base64_rlp = base64.urlsafe_b64encode(rlp.encode(self)) unpadded_base64_rlp = base64_rlp.rstrip(b"=") return "".join(( ENR_REPR_PREFIX, unpadded_base64_rlp.decode("ASCII"), )) IDENTITY_SCHEME_ENR_KEY = b"id" ENR_KEY_SEDES_MAPPING = { b"id": binary, b"secp256k1": Binary.fixed_length(33), b"ip": Binary.fixed_length(IP_V4_SIZE), b"tcp": big_endian_int, b"udp": big_endian_int, b"ip6": Binary.fixed_length(IP_V6_SIZE), b"tcp6": big_endian_int, b"udp6": big_endian_int, # For now this key is used only for ForkIDs (https://eips.ethereum.org/EIPS/eip-2124) but it # is represented as a List because more stuff (e.g. network-id) may be added in the future. b"eth": List([ForkID]), } # Must use raw for values with an unknown key as they may be lists or individual values. FALLBACK_ENR_VALUE_SEDES = raw
(is_bytes, identity), )), "storageKeys": apply_formatter_to_array(hexstr_if_str(to_int)) } ), ), 'maxPriorityFeePerGas': hexstr_if_str(to_int), 'maxFeePerGas': hexstr_if_str(to_int), }, ) # Define typed transaction common sedes. # [[{20 bytes}, [{32 bytes}...]]...], where ... means “zero or more of the thing to the left”. access_list_sede_type = CountableList( List([ Binary.fixed_length(20, allow_empty=False), CountableList(BigEndianInt(32)), ]), ) class _TypedTransactionImplementation(ABC): """ Abstract class that every typed transaction must implement. Should not be imported or used by clients of the library. """ @abstractmethod def hash(self) -> bytes: pass @abstractmethod def payload(self) -> bytes:
class PrePrepare(BaseMessage): content_sedes = List([ big_endian_int, # view big_endian_int, # seqno big_endian_int, # extra # requests, with sha256(b'\x12...') or payload('\x60...') CountableList(raw), raw, # non_det_choices ]) payload_sedes = List([ raw, # content raw, # auth(signature) ]) def __init__(self, view, seqno, extra, requests, non_det_choices): super().__init__() self.view = view self.seqno = seqno self.extra = extra self.requests = requests self.non_det_choices = non_det_choices self.content = None self.auth = None self.payload = None @property def use_signature(self): return True if self.extra & 2 else False @use_signature.setter def use_signature(self, val): if val: self.extra |= 2 else: self.extra &= ~2 @property def consensus_digest(self): """Used to make sure that primary did NOT tamper the requests including all request.consensus_digest and non_det_choices """ d = hashlib.sha256() for r in self.requests: d.update(r.consensus_digest) d.update(self.non_det_choices) return d.digest() @property def content_digest(self): d = hashlib.sha256() d.update('{}'.format(self.view).encode()) d.update('{}'.format(self.seqno).encode()) d.update('{}'.format(self.extra).encode()) for r in self.requests: d.update(r.content_digest) d.update(self.non_det_choices) return d.digest() @property def is_requests_verified(self, node): count = 0 for r in self.requests: if r.verified: count += 1 return count == len(self.requests) def gen_payload(self, primary): raw_requests = [r.payload_in_pre_prepare for r in self.requests] self.content = rlp.encode([self.view, self.seqno, self.extra, raw_requests, self.non_det_choices], cls.content_sedes) self.authenticate(primary) self.payload = rlp.encode([self,content, self.raw_auth], cls.payload_sedes) @classmethod def from_primary(cls, primary, use_signature:bool): extra = 0 if use_signature: extra |= 2 non_det_choices = b'' # TODO: non deterministic choices message = cls(primary.view, primary.seqno, extra, None, non_det_choices) requests = [] # request payload for rs in primary.rw_requests: if rs and rs[-1].pre_prepare_candidate: r = rs.pop(-1) requests.append(r) if len(requests) >= conf.request_in_pre_prepare: break message.requests = requests message.gen_payload(primary) return message @classmethod def from_payload(cls, payload, addr, node): try: [content, auth] = rlp.decode(payload, cls.payload_sedes) [view, seqno, extra, requests, non_det_choices] = rlp.decode( content, cls.content_sedes) for i, r in enumerate(requests): requests[i] = Request.from_payload(r, addr, node) message = cls(view, seqno, extra, requests, non_det_choices) message.content = content if message.use_signature: message.auth = auth else: message.auth = rlp.decode(auth, cls.authenticators_sedes) message.payload = payload message.from_addr = addr return message except rlp.DecodingError as exc: raise ValueError('decoding error: {}'.format(exc))
class Test2(Serializable): fields = ( ('field1', Test1), ('field2', List((Test1, Test1))), )
class Test1(Serializable): fields = (('field1', big_endian_int), ('field2', binary), ('field3', List((big_endian_int, binary))))
class Request(BaseMessage): content_sedes = List([ big_endian_int, # sender index big_endian_int, # timestamp(reqid) big_endian_int, # extra bitmap big_endian_int, # full replier raw, # command or command_digest ]) payload_sedes = List([ raw, # content raw, # auth(authenticators or signature) # maybe empty(b'') if content contains command_digest ]) def __init__(self, sender:int, reqid:Reqid, extra:int, full_replier:int, command:bytes): """ :extra bit 1: 0 readwrite, 1 readonly bit 2: 0 authenticators, 1 signature bit 5: 0 send from replica 1 send from client bit 6: 0 full replier, 1 reply from all this flag is used for there is no -1 in rlp encoding :command set by users :auth bytes(signature) or [hmac_sha256...](authenticators) :full_replier ignored if extra & 1 << 5 is set (reply from all) """ super().__init__(); self.sender = sender self.reqid = reqid self.extra = extra self.full_replier = full_replier self.reply_will_full = False self.command = command self._command_digest = None self.content = None self.auth = None self.payload = None self.from_addr = None @property def readonly(self): return True if self.extra & 1 else False @readonly.setter def readonly(self, val): if val: self.extra |= 1 else: self.extra &= ~1 @property def use_signature(self): return True if self.extra & 2 else False @use_signature.setter def use_signature(self, val): if val: self.extra |= 2 else: self.extra &= ~2 @property def reply_from_all(self): return True if self.extra & 1 << 5 else False @reply_from_all.setter def reply_from_all(self, val): if val: self.extra |= 1 << 5 else: self.extra &= ~(1 << 5) @property def command_digest(self): """compute digest for command""" if self.command: d = hashlib.sha256() # dx: i am going to use the data instead of hahs # so I am commenting them out. # d.update('{}'.format(self.sender).encode()) # useless # d.update('{}'.format(self.reqid).encode()) # useless d.update(self.command) # useful return d.digest() return self._command_digest @property.setter def command_digest(self, new_command_digest): if not self.command: self._command_digest = new_command_digest @property def consensus_digest(self): d = hashlib.sha256() if self.sender_type is 'Client': d.update('{}'.format(1).encode()) else: d.update('{}'.format(0).encode()) d.update('{}'.format(self.sender).encode()) d.update('{}'.format(self.reqid).encode()) d.update(self.command_digest) return d.digest() @property def content_digest(self): d = hashlib.sha256() if self.sender_type is 'Client': d.update('{}'.format(1).encode()) else: d.update('{}'.format(0).encode()) d.update('{}'.format(self.sender).encode()) d.update('{}'.format(self.reqid).encode()) d.update('{}'.format(self.extra).encode()) d.update('{}'.format(self.full_replier).encode()) d.update(self.command_digest) # includes command and len(command) return d.digest() def gen_payload(self, node): self.content = rlp.encode([ self.sender, self.reqid, self.extra, self.full_replier, self.command, ], self.content_sedes) self.authenticate(node) self.payload = rlp.encode([self.content, self.raw_auth], self.payload_sedes) def change_by_primary(self, request, primary): assert request.verified and self.verified changed = False if self.extra != request.extra: self.extra = request.extra changed = True if self.full_replier != request.full_replier: self.full_replier = request.full_replier changed = True if self.auth != request.auth: self.auth = request.auth changed = True # re-authenticated self.reply_with_full = (self.reply_with_full or self.reply_from_all or self.full_replier == primary.index) if changed: self.gen_payload() return changed def change_by_backup(self, request, backup): assert request.verified # only allow verified request if not self.command: self.command = request.command changed = False if self.extra != request.extra: self.extra = request.extra changed = True if self.full_replier != request.full_replier: self.full_replier = request.full_replier changed = True if self.auth != request.auth: self.auth = request.auth changed = True # re-authenticated self.reply_with_full = (self.reply_with_full or self.reply_from_all or self.full_replier == backup.index) self.verified = True # if was False, updated by verified reuqest if changed: self.gen_payload() return changed def verify(self, node, peer_principal): pp = peer_principal if self.verified or not self.auth: pass # already verified or no auth to verify elif self.use_signature: self.verified = pp.verify(self.content_digest, self.auth) else: if len(node.replica_principals) != len(self.auth): self.verifed = False else: # for request, we use 'out' key for the authentication assert pp.index == self.sender self.verified = (pp.gen_hmac('out', self.content_digest) == self.auth[self.sender]) return self.verified @property def payload_in_pre_prepare(self): if len(self.command) <= conf.pre_prepare_big_request_thresh: return self.payload content = rlp.encode([ self.sender, self.reqid, self.extra, self.full_replier, self.command_digest ], message.content_sedes) payload = rlp.encode([content, b''], message.payload_sedes) return payload @classmethod def from_client(cls, node, readonly:bool, use_signature:bool, reply_from_all:bool, full_replier:int, command:bytes): extra = 0 if readonly: extra |= 1 if use_signature: extra |= 2 if node.type is 'Client': extra |= 1 << 4 if reply_from_all: extra |= 1 << 5 message = cls(node.sender, node.next_reqid(), extra, full_replier, command) message.gen_payload() return message @classmethod def from_payload(cls, payload, addr, node): try: [content, auth] = rlp.decode(payload, cls.payload_sedes) [sender, reqid, extra, full_replier, extra, command] = ( rlp.decode(content, cls.content_sedes)) if not auth: # this should be a request inside of a pre_prepare # it has no auth attached, get the full request form client message = cls(sender, reqid, extra, full_replier, None) message.command_digest = command else: # full request message = cls(sender, reqid, extra, full_replier, command) if message.use_signature: message.auth = auth else: message.auth = rlp.decode(auth, cls.authenticators_sedes) message.content = content message.payload = payload if (message.reply_from_all or message.full_replier == node.index): message.reply_with_full = True message.from_addr = addr return message except rlp.DecodingError as exc: raise ValueError('decoding error: {}'.format(exc))