示例#1
0
    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
示例#2
0
    def message_match(self, runner: 'Runner', msg: Message) -> Optional[str]:
        """Does this message match what we expect?"""
        partmessage = Message(self.msgtype, **self.resolve_args(runner, self.kwargs))

        ret = cmp_msg(msg, partmessage)
        if ret is None:
            self.if_match(self, msg)
            msg_to_stash(runner, self, msg)
        return ret
示例#3
0
 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
示例#4
0
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]')
示例#5
0
    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()
示例#6
0
 def _unsigned_channel_announcment(self, features: str,
                                   short_channel_id: str) -> Message:
     """Produce a channel_announcement message with dummy sigs"""
     node_ids = self.node_ids()
     bitcoin_keys = self.funding_pubkeys_for_gossip()
     return Message(event_namespace.get_msgtype('channel_announcement'),
                    node_signature_1=Sig(bytes(64)),
                    node_signature_2=Sig(bytes(64)),
                    bitcoin_signature_1=Sig(bytes(64)),
                    bitcoin_signature_2=Sig(bytes(64)),
                    features=features,
                    chain_hash=self.chain_hash,
                    short_channel_id=short_channel_id,
                    node_id_1=node_ids[0].format(),
                    node_id_2=node_ids[1].format(),
                    bitcoin_key_1=bitcoin_keys[0].format(),
                    bitcoin_key_2=bitcoin_keys[1].format())
示例#7
0
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])
示例#8
0
    def ignore_pings(msg: Message) -> Optional[List[Message]]:
        """Function to ignore pings (and respond with pongs appropriately)"""
        if msg.messagetype.name != 'ping':
            return None

        # BOLT #1:
        # A node receiving a `ping` message:
        # ...
        #  - if `num_pong_bytes` is less than 65532:
        #    - MUST respond by sending a `pong` message, with `byteslen` equal
        #     to `num_pong_bytes`.
        #  - otherwise (`num_pong_bytes` is **not** less than 65532):
        #    - MUST ignore the `ping`.
        if msg.fields['num_pong_bytes'] >= 65532:
            return []

        # A node sending a `pong` message:
        #  - SHOULD set `ignored` to 0s.
        #  - MUST NOT set `ignored` to sensitive data such as secrets or
        #    portions of initialized
        outmsg = Message(event_namespace.get_msgtype('pong'),
                         ignored='00' * msg.fields['num_pong_bytes'])
        return [outmsg]
示例#9
0
    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