package com.ripple.core.types.known.tx.signed; import com.ripple.core.coretypes.Amount; import com.ripple.core.coretypes.Blob; import com.ripple.core.coretypes.PathSet; import com.ripple.core.coretypes.hash.HalfSha512; import com.ripple.core.coretypes.hash.Hash256; import com.ripple.core.coretypes.hash.prefixes.HashPrefix; import com.ripple.core.coretypes.uint.UInt32; import com.ripple.core.fields.Field; import com.ripple.core.serialized.BytesList; import com.ripple.core.serialized.MultiSink; import com.ripple.core.serialized.SerializedType; import com.ripple.core.serialized.enums.TransactionType; import com.ripple.core.types.known.tx.Transaction; import com.ripple.crypto.ecdsa.IKeyPair; import com.ripple.crypto.ecdsa.Seed; public class SignedTransaction { private SignedTransaction(Transaction of) { // TODO: is this just over kill ? // TODO: do we want to -> binary -> Transaction ? // Shallow copy the transaction. Child fields // are practically immutable, except perhaps PathSet. txn = new Transaction(of.transactionType()); for (Field field : of) { SerializedType st = of.get(field); // Deep copy Paths if (field == Field.Paths) { st = PathSet.translate.fromBytes(st.toBytes()); } txn.put(field, st); } } // This will eventually be private @Deprecated public SignedTransaction() {} public Transaction txn; public Hash256 hash; public Hash256 signingHash; public Hash256 previousSigningHash; public String tx_blob; public void sign(String base58Secret) { sign(Seed.fromBase58(base58Secret).keyPair()); } public static SignedTransaction fromTx(Transaction tx) { return new SignedTransaction(tx); } public void sign(IKeyPair keyPair) { prepare(keyPair, null, null, null); } public void prepare(IKeyPair keyPair, Amount fee, UInt32 Sequence, UInt32 lastLedgerSequence) { Blob pubKey = new Blob(keyPair.pubBytes()); // This won't always be specified if (lastLedgerSequence != null) { txn.put(UInt32.LastLedgerSequence, lastLedgerSequence); } if (Sequence != null) { txn.put(UInt32.Sequence, Sequence); } if (fee != null) { txn.put(Amount.Fee, fee); } txn.signingPubKey(pubKey); if (Transaction.CANONICAL_FLAG_DEPLOYED) { txn.setCanonicalSignatureFlag(); } txn.checkFormat(); signingHash = txn.signingHash(); if (previousSigningHash != null && signingHash.equals(previousSigningHash)) { return; } try { txn.txnSignature(new Blob(keyPair.sign(signingHash.bytes()))); BytesList blob = new BytesList(); HalfSha512 id = HalfSha512.prefixed256(HashPrefix.transactionID); txn.toBytesSink(new MultiSink(blob, id)); tx_blob = blob.bytesHex(); hash = id.finish(); } catch (Exception e) { // electric paranoia previousSigningHash = null; throw new RuntimeException(e); } /*else {*/ previousSigningHash = signingHash; // } } public TransactionType transactionType() { return txn.transactionType(); } }