Class Transaction
- Direct Known Subclasses:
BlockchainRestController.PaymentTransaction
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 -
Constructor Summary
ConstructorsModifierConstructorDescriptionprotectedTransaction(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 TypeMethodDescriptionbooleanTwo transactions are equal if and only if their IDs are equal.Returns the token amount transferred by this transaction.getId()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.inthashCode()Hash code based solely on the transaction ID.booleanisSigned()Returnstrueif a signature has been attached to this transaction.voidsign(byte[] signatureBytes) Attaches the ECDSA signature to this transaction.byte[]Returns the byte array that the sender MUST sign and verifiers MUST verify.toString()Returns a human-readable summary of the transaction (safe for logging).
-
Field Details
-
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; callsign(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 nullIllegalArgumentException- 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
finalso 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
signatureBytesarray is defensively copied to prevent external mutation after signing.- Parameters:
signatureBytes- DER-encoded ECDSA signature produced by the sender's private key overtoSignableBytes()(non-null, non-empty)- Throws:
NullPointerException- if signatureBytes is nullIllegalArgumentException- 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
Returns the unique identifier of this transaction.- Returns:
- non-null UUID
-
getSenderAddress
Returns the sender's blockchain address.- Returns:
- non-null, non-blank hex-encoded address
-
getReceiverAddress
Returns the receiver's blockchain address.- Returns:
- non-null, non-blank hex-encoded address
-
getAmount
Returns the token amount transferred by this transaction.- Returns:
- non-null, non-negative
BigDecimal
-
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
nullif the transaction has not yet been signed.- Returns:
- copy of signature bytes, or
nullif unsigned
-
isSigned
public boolean isSigned()Returnstrueif a signature has been attached to this transaction.- Returns:
trueif signed,falseotherwise
-
getMetadata
Returns an unmodifiable view of the transaction metadata map.SpotBugs flags this as
EI_EXPOSE_REPbecause aMapis a mutable type. In practice the field is always assigned aCollections.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@SuppressFBWarningsdocuments this deliberate design.- Returns:
- non-null, unmodifiable metadata map (maybe empty)
-
equals
Two transactions are equal if and only if their IDs are equal. -
hashCode
public int hashCode()Hash code based solely on the transaction ID.- Overrides:
hashCodein classObject- Returns:
- hash code consistent with
equals(Object)
-
toString
Returns a human-readable summary of the transaction (safe for logging).The signature bytes are intentionally omitted from this output.
-