def node_announcement(self, side: Side, features: str, rgb_color: Tuple[int, int, int], alias: str, addresses: bytes, timestamp: int) -> Message: # Begin with a fake signature. ann = Message(namespace().get_msgtype('node_announcement'), signature=Sig(bytes(64)), features=features, timestamp=timestamp, node_id=self.node_id(side).format().hex(), rgb_color=bytes(rgb_color).hex(), alias=bytes(alias, encoding='utf-8').zfill(32), addresses=addresses) # BOLT #7: # - MUST set `signature` to the signature of the double-SHA256 of the entire # remaining packet after `signature` (using the key given by `node_id`). buf = io.BytesIO() ann.write(buf) # Note the first two 'type' bytes! h = sha256(sha256(buf.getvalue()[2 + 64:]).digest()).digest() ann.set_field('signature', Sig(self.node_privkeys[side].secret.hex(), h.hex())) return ann
def action(self, runner: 'Runner') -> bool: super().action(runner) # Now we have runner, we can fill in all the message fields message = Message(self.msgtype, **self.resolve_args(runner, self.kwargs)) missing = message.missing_fields() if missing: raise SpecFileError(self, "Missing fields {}".format(missing)) binmsg = io.BytesIO() message.write(binmsg) runner.recv(self, self.find_conn(runner), binmsg.getvalue()) msg_to_stash(runner, self, message) return True
def get_output_message(self, conn: Conn, event: ExpectMsg) -> Optional[bytes]: if self.config.getoption('verbose'): print("[GET_OUTPUT_MESSAGE {}]".format(conn)) # We make the message they were expecting. msg = Message(event.msgtype, **event.resolve_args(self, event.kwargs)) # Fake up the other fields. for m in msg.missing_fields(): ftype = msg.messagetype.find_field(m.name) msg.set_field(m.name, self.fake_field(ftype.fieldtype)) binmsg = io.BytesIO() msg.write(binmsg) return binmsg.getvalue()
def test_message_constructor(): ns = MessageNamespace([ 'msgtype,test1,1', 'msgdata,test1,tlvs,test_tlvstream,', 'tlvtype,test_tlvstream,tlv1,1', 'tlvdata,test_tlvstream,tlv1,field1,byte,4', 'tlvdata,test_tlvstream,tlv1,field2,u32,', 'tlvtype,test_tlvstream,tlv2,255', 'tlvdata,test_tlvstream,tlv2,field3,byte,...' ]) m = Message(ns.get_msgtype('test1'), tlvs='{tlv1={field1=01020304,field2=5}' ',tlv2={field3=01020304},4=010203}') buf = io.BytesIO() m.write(buf) assert buf.getvalue() == bytes([0, 1] + [1, 8, 1, 2, 3, 4, 0, 0, 0, 5] + [4, 3, 1, 2, 3] + [253, 0, 255, 4, 1, 2, 3, 4])
def calc_checksum(update: Message) -> int: # BOLT #7: The checksum of a `channel_update` is the CRC32C checksum as # specified in [RFC3720](https://tools.ietf.org/html/rfc3720#appendix-B.4) # of this `channel_update` without its `signature` and `timestamp` fields. bufio = io.BytesIO() update.write(bufio) buf = bufio.getvalue() # BOLT #7: # 1. type: 258 (`channel_update`) # 2. data: # * [`signature`:`signature`] # * [`chain_hash`:`chain_hash`] # * [`short_channel_id`:`short_channel_id`] # * [`u32`:`timestamp`] # * [`byte`:`message_flags`] # Note: 2 bytes for `type` field return crc32c.crc32(buf[2 + 64:2 + 64 + 32 + 8] + buf[2 + 64 + 32 + 8 + 4:])
def test_dynamic_array(): """Test that dynamic array types enforce matching lengths""" ns = MessageNamespace([ 'msgtype,test1,1', 'msgdata,test1,count,u16,', 'msgdata,test1,arr1,byte,count', 'msgdata,test1,arr2,u32,count' ]) # This one is fine. m = Message(ns.get_msgtype('test1'), arr1='01020304', arr2='[1,2,3,4]') buf = io.BytesIO() m.write(buf) assert buf.getvalue() == bytes( [0, 1] + [0, 4] + [1, 2, 3, 4] + [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4]) # These ones are not with pytest.raises(ValueError, match='Inconsistent length.*count'): m = Message(ns.get_msgtype('test1'), arr1='01020304', arr2='[1,2,3]') with pytest.raises(ValueError, match='Inconsistent length.*count'): m = Message(ns.get_msgtype('test1'), arr1='01020304', arr2='[1,2,3,4,5]')
def channel_update(self, short_channel_id: str, side: Side, disable: bool, cltv_expiry_delta: int, htlc_minimum_msat: int, fee_base_msat: int, fee_proportional_millionths: int, timestamp: int, htlc_maximum_msat: Optional[int]) -> Message: # BOLT #7: The `channel_flags` bitfield is used to indicate the # direction of the channel: it identifies the node that this update # originated from and signals various options concerning the # channel. The following table specifies the meaning of its individual # bits: # # | Bit Position | Name | Meaning | # | ------------- | ----------- | -------------------------------- | # | 0 | `direction` | Direction this update refers to. | # | 1 | `disable` | Disable the channel. | # BOLT #7: # - if the origin node is `node_id_1` in the message: # - MUST set the `direction` bit of `channel_flags` to 0. # - otherwise: # - MUST set the `direction` bit of `channel_flags` to 1. if self.funding_pubkey(side) == self.funding_pubkeys_for_gossip()[0]: channel_flags = 0 else: channel_flags = 1 if disable: channel_flags |= 2 # BOLT #7: The `message_flags` bitfield is used to indicate the # presence of optional fields in the `channel_update` message: # # | Bit Position | Name | Field | # | ------------- | ------------------------- | -------------------------------- | # | 0 | `option_channel_htlc_max` | `htlc_maximum_msat` | message_flags = 0 if htlc_maximum_msat: message_flags |= 1 # Begin with a fake signature. update = Message(namespace().get_msgtype('channel_update'), short_channel_id=short_channel_id, signature=Sig(bytes(64)), chain_hash=self.chain_hash, timestamp=timestamp, message_flags=message_flags, channel_flags=channel_flags, cltv_expiry_delta=cltv_expiry_delta, htlc_minimum_msat=htlc_minimum_msat, fee_base_msat=fee_base_msat, fee_proportional_millionths=fee_proportional_millionths) if htlc_maximum_msat: update.set_field('htlc_maximum_msat', htlc_maximum_msat) # BOLT #7: # - MUST set `signature` to the signature of the double-SHA256 of the # entire remaining packet after `signature`, using its own `node_id`. buf = io.BytesIO() update.write(buf) # Note the first two 'type' bytes! h = sha256(sha256(buf.getvalue()[2 + 64:]).digest()).digest() update.set_field('signature', Sig(self.node_privkeys[side].secret.hex(), h.hex())) return update