A transaction
| 344 | |
| 345 | |
| 346 | class CTransaction(ImmutableSerializable): |
| 347 | """A transaction""" |
| 348 | __slots__ = ['nVersion', 'vin', 'vout', 'nLockTime', 'wit'] |
| 349 | |
| 350 | def __init__(self, vin=(), vout=(), nLockTime=0, nVersion=1, witness=CTxWitness()): |
| 351 | """Create a new transaction |
| 352 | |
| 353 | vin and vout are iterables of transaction inputs and outputs |
| 354 | respectively. If their contents are not already immutable, immutable |
| 355 | copies will be made. |
| 356 | """ |
| 357 | if not (0 <= nLockTime <= 0xffffffff): |
| 358 | raise ValueError('CTransaction: nLockTime must be in range 0x0 to 0xffffffff; got %x' % nLockTime) |
| 359 | object.__setattr__(self, 'nLockTime', nLockTime) |
| 360 | object.__setattr__(self, 'nVersion', nVersion) |
| 361 | object.__setattr__(self, 'vin', tuple(CTxIn.from_txin(txin) for txin in vin)) |
| 362 | object.__setattr__(self, 'vout', tuple(CTxOut.from_txout(txout) for txout in vout)) |
| 363 | object.__setattr__(self, 'wit', CTxWitness.from_txwitness(witness)) |
| 364 | |
| 365 | @classmethod |
| 366 | def stream_deserialize(cls, f): |
| 367 | """Deserialize transaction |
| 368 | |
| 369 | This implementation corresponds to Bitcoin's SerializeTransaction() and |
| 370 | consensus behavior. Note that Bitcoin's DecodeHexTx() also has the |
| 371 | option to attempt deserializing as a non-witness transaction first, |
| 372 | falling back to the consensus behavior if it fails. The difference lies |
| 373 | in transactions which have zero inputs: they are invalid but may be |
| 374 | (de)serialized anyway for the purpose of signing them and adding |
| 375 | inputs. If the behavior of DecodeHexTx() is needed it could be added, |
| 376 | but not here. |
| 377 | """ |
| 378 | # FIXME can't assume f is seekable |
| 379 | nVersion = struct.unpack(b"<i", ser_read(f,4))[0] |
| 380 | pos = f.tell() |
| 381 | markerbyte = struct.unpack(b'B', ser_read(f, 1))[0] |
| 382 | flagbyte = struct.unpack(b'B', ser_read(f, 1))[0] |
| 383 | if markerbyte == 0 and flagbyte == 1: |
| 384 | vin = VectorSerializer.stream_deserialize(CTxIn, f) |
| 385 | vout = VectorSerializer.stream_deserialize(CTxOut, f) |
| 386 | wit = CTxWitness(tuple(0 for dummy in range(len(vin)))) |
| 387 | wit = wit.stream_deserialize(f) |
| 388 | nLockTime = struct.unpack(b"<I", ser_read(f,4))[0] |
| 389 | return cls(vin, vout, nLockTime, nVersion, wit) |
| 390 | else: |
| 391 | f.seek(pos) # put marker byte back, since we don't have peek |
| 392 | vin = VectorSerializer.stream_deserialize(CTxIn, f) |
| 393 | vout = VectorSerializer.stream_deserialize(CTxOut, f) |
| 394 | nLockTime = struct.unpack(b"<I", ser_read(f,4))[0] |
| 395 | return cls(vin, vout, nLockTime, nVersion) |
| 396 | |
| 397 | |
| 398 | def stream_serialize(self, f, include_witness=True): |
| 399 | f.write(struct.pack(b"<i", self.nVersion)) |
| 400 | if include_witness and not self.wit.is_null(): |
| 401 | assert(len(self.wit.vtxinwit) <= len(self.vin)) |
| 402 | f.write(b'\x00') # Marker |
| 403 | f.write(b'\x01') # Flag |
no outgoing calls