Class Transaction

java.lang.Object
com.privatechain.core.model.Transaction
Direct Known Subclasses:
BlockchainRestController.PaymentTransaction

public abstract class Transaction extends Object
Abstract base class for all blockchain transactions.

Consumers must subclass this class to define their own transaction types. The six fields declared here are present on every transaction regardless of business logic. Subclasses add domain-specific fields (e.g., asset ID, contract parameters) and may carry arbitrary structured data via the protected metadata map without further subclassing (FR-CORE-02).

Why abstract, not interface?

Interfaces cannot hold fields, yet library code must safely access senderAddress, signature, etc. on any Transaction without casting. An abstract class guarantees these fields exist at the cost of requiring single-inheritance — an acceptable trade-off for a value-object hierarchy (see design.md §7.2).

Polymorphic serialization

@JsonTypeInfo writes the concrete class name into every JSON payload under the _type field. This ensures that a Transaction subclass survives a full JSON round-trip on any node that has the subclass on its classpath (FR-SER-01, AC-09).


 // Example subclass:
 public class PaymentTransaction extends Transaction {
     private final String currency;
     // ...
 }
 
Since:
1.0.0
See Also:
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    protected final Map<String,Object>
    Flexible key-value map for lightweight extension.
  • Constructor Summary

    Constructors
    Modifier
    Constructor
    Description
    protected
    Transaction(UUID id, String senderAddress, String receiverAddress, BigDecimal amount, Instant timestamp, Map<String,Object> metadata)
    Constructs a new unsigned transaction with all required fields.
  • Method Summary

    Modifier and Type
    Method
    Description
    boolean
    Two transactions are equal if and only if their IDs are equal.
    Returns the token amount transferred by this transaction.
    Returns the unique identifier of this transaction.
    Returns an unmodifiable view of the transaction metadata map.
    Returns the receiver's blockchain address.
    Returns the sender's blockchain address.
    byte[]
    Returns a defensive copy of the ECDSA signature bytes.
    Returns the UTC creation timestamp of this transaction.
    int
    Hash code based solely on the transaction ID.
    boolean
    Returns true if a signature has been attached to this transaction.
    void
    sign(byte[] signatureBytes)
    Attaches the ECDSA signature to this transaction.
    byte[]
    Returns the byte array that the sender MUST sign and verifiers MUST verify.
    Returns a human-readable summary of the transaction (safe for logging).

    Methods inherited from class java.lang.Object

    clone, finalize, getClass, notify, notifyAll, wait, wait, wait
  • Field Details

    • metadata

      protected final Map<String,Object> metadata
      Flexible key-value map for lightweight extension.

      Subclasses that only need to attach a few extra string/number values can use this map rather than creating a new subclass. However, for complex or type-safe extensions, a dedicated subclass is preferred.

      The map is stored as an unmodifiable view once the transaction is built, but the underlying map may be mutated before sign(byte[]) is called.

  • Constructor Details

    • Transaction

      protected Transaction(UUID id, String senderAddress, String receiverAddress, BigDecimal amount, Instant timestamp, Map<String,Object> metadata)
      Constructs a new unsigned transaction with all required fields.

      The signature is initialized to null; call sign(byte[]) after construction to attach a valid signature before submitting to the network.

      Parameters:
      id - unique transaction identifier (non-null)
      senderAddress - hex-encoded sender address (non-null, non-blank)
      receiverAddress - hex-encoded receiver address (non-null, non-blank)
      amount - token amount (≥ 0; non-null)
      timestamp - creation time in UTC (non-null)
      metadata - arbitrary key-value metadata; may be null (treated as empty)
      Throws:
      NullPointerException - if id, senderAddress, receiverAddress, amount, or timestamp is null
      IllegalArgumentException - if senderAddress or receiverAddress is blank, or if amount is negative
  • Method Details

    • sign

      public void sign(byte[] signatureBytes)
      Attaches the ECDSA signature to this transaction.

      This method is intentionally not final so that subclasses can override it to sign additional domain-specific fields. Callers must invoke the super implementation to set the base signature bytes.

      Security note: The raw signatureBytes array is defensively copied to prevent external mutation after signing.

      Parameters:
      signatureBytes - DER-encoded ECDSA signature produced by the sender's private key over toSignableBytes() (non-null, non-empty)
      Throws:
      NullPointerException - if signatureBytes is null
      IllegalArgumentException - if signatureBytes is empty
    • toSignableBytes

      public byte[] toSignableBytes()
      Returns the byte array that the sender MUST sign and verifiers MUST verify.

      The default implementation concatenates the canonical string representations of the core fields. Subclasses that add signable fields must override this method and append their own field bytes after calling super.toSignableBytes().

      Returns:
      deterministic byte array over which the signature was (or will be) computed
    • getId

      public UUID getId()
      Returns the unique identifier of this transaction.
      Returns:
      non-null UUID
    • getSenderAddress

      public String getSenderAddress()
      Returns the sender's blockchain address.
      Returns:
      non-null, non-blank hex-encoded address
    • getReceiverAddress

      public String getReceiverAddress()
      Returns the receiver's blockchain address.
      Returns:
      non-null, non-blank hex-encoded address
    • getAmount

      public BigDecimal getAmount()
      Returns the token amount transferred by this transaction.
      Returns:
      non-null, non-negative BigDecimal
    • getTimestamp

      public Instant getTimestamp()
      Returns the UTC creation timestamp of this transaction.
      Returns:
      non-null Instant
    • getSignature

      public byte[] getSignature()
      Returns a defensive copy of the ECDSA signature bytes.

      Returns null if the transaction has not yet been signed.

      Returns:
      copy of signature bytes, or null if unsigned
    • isSigned

      public boolean isSigned()
      Returns true if a signature has been attached to this transaction.
      Returns:
      true if signed, false otherwise
    • getMetadata

      public Map<String,Object> getMetadata()
      Returns an unmodifiable view of the transaction metadata map.

      SpotBugs flags this as EI_EXPOSE_REP because a Map is a mutable type. In practice the field is always assigned a Collections.unmodifiableMap(java.util.Map<? extends K, ? extends V>) wrapper in the constructor, so the returned reference cannot be used to mutate the stored data. The @SuppressFBWarnings documents this deliberate design.

      Returns:
      non-null, unmodifiable metadata map (maybe empty)
    • equals

      public boolean equals(Object obj)
      Two transactions are equal if and only if their IDs are equal.
      Overrides:
      equals in class Object
      Parameters:
      obj - object to compare
      Returns:
      true if obj is a Transaction with the same ID
    • hashCode

      public int hashCode()
      Hash code based solely on the transaction ID.
      Overrides:
      hashCode in class Object
      Returns:
      hash code consistent with equals(Object)
    • toString

      public String toString()
      Returns a human-readable summary of the transaction (safe for logging).

      The signature bytes are intentionally omitted from this output.

      Overrides:
      toString in class Object
      Returns:
      string representation