Add an example script on assembling a message
This commit is contained in:
parent
1db96e2e27
commit
43ba487d67
221
assemble_msg.py
Normal file
221
assemble_msg.py
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from struct import pack
|
||||||
|
|
||||||
|
from pybitmessage import (
|
||||||
|
defaults, helper_msgcoding, highlevelcrypto, protocol, shared)
|
||||||
|
from pybitmessage.addresses import (
|
||||||
|
decodeAddress, decodeVarint, encodeAddress, encodeVarint)
|
||||||
|
from pybitmessage.fallback import RIPEMD160Hash
|
||||||
|
from pybitmessage.helper_ackPayload import genAckPayload
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('default')
|
||||||
|
|
||||||
|
|
||||||
|
class Address(object):
|
||||||
|
version = 4
|
||||||
|
stream = 1
|
||||||
|
sigkey = None
|
||||||
|
enkey = None
|
||||||
|
ripe = None
|
||||||
|
bitfield = pack('>I', 0)
|
||||||
|
avg_pow = defaults.networkDefaultProofOfWorkNonceTrialsPerByte
|
||||||
|
avg_extra_bytes = defaults.networkDefaultPayloadLengthExtraBytes
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromData(cls, data, addr=None):
|
||||||
|
# parse addr_data
|
||||||
|
# SELECT transmitdata FROM pubkeys WHERE address = ?
|
||||||
|
version, _ = decodeVarint(data[0])
|
||||||
|
pos = 1
|
||||||
|
stream, length = decodeVarint(data[pos:pos + 10])
|
||||||
|
pos += length
|
||||||
|
bitfield = data[pos:pos + 4]
|
||||||
|
# TODO: mobile
|
||||||
|
pos += 4
|
||||||
|
sigkey = data[pos:pos + 64]
|
||||||
|
pos += 64
|
||||||
|
enkey = data[pos:pos + 64]
|
||||||
|
pos += 64
|
||||||
|
|
||||||
|
if version >= 3:
|
||||||
|
avg_pow = decodeVarint(data[pos:pos + 10])
|
||||||
|
pos += length
|
||||||
|
extra_bytes, length = decodeVarint(data[pos:pos + 10])
|
||||||
|
avg_pow = max(
|
||||||
|
defaults.networkDefaultProofOfWorkNonceTrialsPerByte, avg_pow)
|
||||||
|
extra_bytes = max(
|
||||||
|
defaults.networkDefaultPayloadLengthExtraBytes, extra_bytes)
|
||||||
|
else:
|
||||||
|
avg_pow = None
|
||||||
|
extra_bytes = None
|
||||||
|
return cls(
|
||||||
|
version, stream, sigkey, enkey,
|
||||||
|
addr=addr, avg_pow=avg_pow, extra_bytes=extra_bytes,
|
||||||
|
bitfield=bitfield)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, version, stream, sigkey, enkey, addr=None, ripe=None,
|
||||||
|
avg_pow=None, extra_bytes=None, bitfield=None
|
||||||
|
):
|
||||||
|
self.version = version
|
||||||
|
self.stream = stream
|
||||||
|
self.sigkey = sigkey
|
||||||
|
self.enkey = enkey
|
||||||
|
if bitfield:
|
||||||
|
self.bitfield = bitfield
|
||||||
|
if ripe is None:
|
||||||
|
if addr:
|
||||||
|
status, dversion, dstream, ripe = decodeAddress(addr)
|
||||||
|
assert status == 'success'
|
||||||
|
assert version == dversion
|
||||||
|
assert stream == dstream
|
||||||
|
else:
|
||||||
|
_sha = hashlib.new('sha512')
|
||||||
|
_sha.update(sigkey + enkey)
|
||||||
|
ripe = RIPEMD160Hash(_sha.digest()).digest()
|
||||||
|
self.ripe = ripe
|
||||||
|
if avg_pow:
|
||||||
|
self.avg_pow = avg_pow
|
||||||
|
if extra_bytes:
|
||||||
|
self.extra_bytes = extra_bytes
|
||||||
|
self.addr = addr or encodeAddress(self.version, self.stream, self.ripe)
|
||||||
|
|
||||||
|
def encrypt(self, data):
|
||||||
|
return highlevelcrypto.encrypt(data, b'04' + hexlify(self.enkey))
|
||||||
|
|
||||||
|
|
||||||
|
class Identity(Address):
|
||||||
|
priv_sigkey = None
|
||||||
|
priv_enkey = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromWIF(cls, sigkey, enkey, bitfield=pack('>I', 0)):
|
||||||
|
return cls(
|
||||||
|
hexlify(shared.decodeWalletImportFormat(sigkey)),
|
||||||
|
hexlify(shared.decodeWalletImportFormat(enkey)), bitfield=bitfield)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fromConfig(cls, addr, config):
|
||||||
|
sigkey_base58 = config.get(addr, 'privsigningkey')
|
||||||
|
enkey_base58 = config.get(addr, 'privencryptionkey')
|
||||||
|
bitfield = 0
|
||||||
|
# send ack
|
||||||
|
if not config.safeGetBoolean(addr, 'dontsendack'):
|
||||||
|
bitfield |= protocol.BITFIELD_DOESACK
|
||||||
|
return cls.fromWIF(
|
||||||
|
sigkey_base58, enkey_base58, bitfield=pack('>I', bitfield))
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, sigkey, enkey, addr=None, bitfield=None,
|
||||||
|
version=None, stream=None, ripe=None,
|
||||||
|
avg_pow=None, extra_bytes=None
|
||||||
|
):
|
||||||
|
self.priv_sigkey = sigkey
|
||||||
|
self.priv_enkey = enkey
|
||||||
|
if version:
|
||||||
|
self.version = version
|
||||||
|
if stream:
|
||||||
|
self.stream = stream
|
||||||
|
sigkey = unhexlify(highlevelcrypto.privToPub(sigkey))[1:]
|
||||||
|
enkey = unhexlify(highlevelcrypto.privToPub(enkey))[1:]
|
||||||
|
super(Identity, self).__init__(
|
||||||
|
self.version, self.stream, sigkey, enkey,
|
||||||
|
addr, ripe, avg_pow, extra_bytes)
|
||||||
|
|
||||||
|
def sign(self, data):
|
||||||
|
return highlevelcrypto.sign(data, self.priv_sigkey)
|
||||||
|
|
||||||
|
def decrypt(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def assemble_msg(
|
||||||
|
fromid, toaddr, subject, message,
|
||||||
|
friend=False, ttl=86400, encoding=2
|
||||||
|
):
|
||||||
|
|
||||||
|
if ttl > 28 * 24 * 60 * 60:
|
||||||
|
ttl = 28 * 24 * 60 * 60
|
||||||
|
# add some randomness to the TTL
|
||||||
|
ttl = int(ttl + random.randrange(-300, 300))
|
||||||
|
embedded_time = int(time.time() + ttl)
|
||||||
|
|
||||||
|
payload = encodeVarint(fromid.version)
|
||||||
|
payload += encodeVarint(fromid.stream)
|
||||||
|
payload += fromid.bitfield
|
||||||
|
|
||||||
|
payload += fromid.sigkey + fromid.enkey
|
||||||
|
|
||||||
|
if fromid.version >= 3:
|
||||||
|
payload += encodeVarint(
|
||||||
|
defaults.networkDefaultProofOfWorkNonceTrialsPerByte if friend
|
||||||
|
else fromid.avg_pow)
|
||||||
|
payload += encodeVarint(
|
||||||
|
defaults.networkDefaultPayloadLengthExtraBytes if friend
|
||||||
|
else fromid.extra_bytes)
|
||||||
|
|
||||||
|
payload += toaddr.ripe
|
||||||
|
payload += encodeVarint(encoding)
|
||||||
|
encoded_message = helper_msgcoding.MsgEncode(
|
||||||
|
{"subject": subject, "body": message}, encoding)
|
||||||
|
payload += encodeVarint(encoded_message.length)
|
||||||
|
payload += encoded_message.data
|
||||||
|
if not protocol.checkBitfield(
|
||||||
|
toaddr.bitfield, protocol.BITFIELD_DOESACK
|
||||||
|
):
|
||||||
|
logger.info(
|
||||||
|
'Not bothering to include ackdata because'
|
||||||
|
' the receiver said that they won\'t relay it anyway.'
|
||||||
|
)
|
||||||
|
ack_payload = ''
|
||||||
|
else:
|
||||||
|
if ttl < 24 * 60 * 60:
|
||||||
|
ttl = 24 * 60 * 60 # 1 day
|
||||||
|
elif ttl < 7 * 24 * 60 * 60:
|
||||||
|
ttl = 7 * 24 * 60 * 60 # 1 week
|
||||||
|
else:
|
||||||
|
ttl = 28 * 24 * 60 * 60 # 4 weeks
|
||||||
|
ttl = int(ttl + random.randrange(-300, 300))
|
||||||
|
ack_payload = pack('>Q', int(time.time() + ttl)) + \
|
||||||
|
genAckPayload(toaddr.stream)
|
||||||
|
|
||||||
|
ack_payload = protocol.CreatePacket('object', ack_payload)
|
||||||
|
|
||||||
|
payload += encodeVarint(len(ack_payload))
|
||||||
|
payload += ack_payload
|
||||||
|
|
||||||
|
logger.info('Payload: %s', hexlify(payload))
|
||||||
|
|
||||||
|
header_tmp = pack('>Q', embedded_time) + '\x00\x00\x00\x02' + \
|
||||||
|
encodeVarint(1) + encodeVarint(toaddr.stream)
|
||||||
|
|
||||||
|
signature = fromid.sign(header_tmp + payload)
|
||||||
|
payload += encodeVarint(len(signature))
|
||||||
|
payload += signature
|
||||||
|
|
||||||
|
encrypted = toaddr.encrypt(payload)
|
||||||
|
|
||||||
|
encrypted_payload = header_tmp + encrypted
|
||||||
|
|
||||||
|
return encrypted_payload
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
fromid = Identity(
|
||||||
|
'93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665',
|
||||||
|
'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a',
|
||||||
|
'BM-onkVu1KKL2UaUss5Upg9vXmqd3esTmV79', version=2)
|
||||||
|
# TODO: generate -> [chan] test
|
||||||
|
toaddr = Identity.fromWIF(
|
||||||
|
'5JY1CFeeyN4eyfL35guWAuUqu5VLmd7LojtkNP6wmt5msZxxZ57',
|
||||||
|
'5J1oDgZDicNhUgbfzBDQqi2m5jUPnDrfZinnTqEEEaLv63jVFTM')
|
||||||
|
print(hexlify(
|
||||||
|
assemble_msg(
|
||||||
|
fromid, toaddr,
|
||||||
|
'hello', 'The quick brown fox jumps over the lazy dog.', True)
|
||||||
|
))
|
Loading…
Reference in New Issue
Block a user