001package org.consensusj.bitcoin.json.pojo;
002
003import com.fasterxml.jackson.annotation.JsonCreator;
004import com.fasterxml.jackson.annotation.JsonProperty;
005import org.bitcoinj.base.Address;
006import org.bitcoinj.core.LockTime;
007import org.bitcoinj.script.Script;
008import org.consensusj.bitcoin.json.conversion.HexUtil;
009import org.bitcoinj.base.Coin;
010import org.bitcoinj.base.Sha256Hash;
011import org.bitcoinj.core.Transaction;
012
013import java.time.Instant;
014import java.util.ArrayList;
015import java.util.List;
016import java.util.stream.Collectors;
017
018/**
019 * RawTransaction POJO
020 */
021// "hash" property added (present in Bitcoin 0.13)
022public class RawTransactionInfo {
023    private final String hex;
024    private final Sha256Hash txid;
025    private final long version;
026    private final LockTime lockTime;
027    private final List<Vin> vin;
028    private final List<Vout> vout;
029    private final Sha256Hash blockhash;
030    private final int confirmations;
031    private final Instant time;
032    public final Instant blocktime;
033
034    @JsonCreator
035    public RawTransactionInfo(@JsonProperty("hex")              String hex,
036                              @JsonProperty("txid")             Sha256Hash txid,
037                              @JsonProperty("version")          long version,
038                              @JsonProperty("locktime")         long locktime,
039                              @JsonProperty("vin")              VinList vin,
040                              @JsonProperty("vout")             VoutList vout,
041                              @JsonProperty("blockhash")        Sha256Hash blockhash,
042                              @JsonProperty("confirmations")    int confirmations,
043                              @JsonProperty("time")             long time,
044                              @JsonProperty("blocktime")        long blocktime) {
045        this.hex = hex;
046        this.txid = txid;
047        this.version = version;
048        this.lockTime = LockTime.of(locktime);
049        this.vin = vin;
050        this.vout = vout;
051        this.blockhash = blockhash;
052        this.confirmations = confirmations;
053        this.time = Instant.ofEpochSecond(time);
054        this.blocktime = Instant.ofEpochSecond(blocktime);
055    }
056
057    /**
058     * Construct from a bitcoinj transaction
059     * @param transaction A bitcoinj confirmed or unconfirmed transaction
060     */
061    public RawTransactionInfo(Transaction transaction) {
062        this.hex = HexUtil.bytesToHexString(transaction.bitcoinSerialize());
063        this.txid = transaction.getTxId();
064        this.version = transaction.getVersion();
065        this.lockTime = transaction.lockTime();
066        this.blockhash = null;  // For now
067        this.confirmations = transaction.getConfidence().getDepthInBlocks();
068        this.time = Instant.ofEpochSecond(0); // TODO: block header time of block including transaction
069        this.blocktime = this.time; // same as time (see API doc)
070        vin = transaction.getInputs().stream()
071                .map(input -> new Vin(txid,
072                        input.getOutpoint().getIndex(),
073                        input.getScriptSig().toString(),
074                        input.getSequenceNumber())
075                )
076                .collect(Collectors.toList());
077        vout = transaction.getOutputs().stream()
078                .map(output -> new Vout(output.getValue(),
079                        output.getIndex(),
080                        new ScriptPubKeyInfo(output.getScriptPubKey()))
081                )
082                .collect(Collectors.toList());
083    }
084
085    public String getHex() {
086        return hex;
087    }
088
089    public Sha256Hash getTxid() {
090        return txid;
091    }
092
093    public long getVersion() {
094        return version;
095    }
096
097    public LockTime getLockTime() {
098        return lockTime;
099    }
100
101    /**
102     * @return lock time
103     * @deprecated Use {@link #getLockTime()}
104     */
105    @Deprecated
106    public long getLocktime() {
107        return getLockTime().rawValue();
108    }
109
110    public List<Vin> getVin() {
111        return vin;
112    }
113
114    public List<Vout> getVout() {
115        return vout;
116    }
117
118    public Sha256Hash getBlockhash() {
119        return blockhash;
120    }
121
122    public int getConfirmations() {
123        return confirmations;
124    }
125
126    public Instant getTime() {
127        return time;
128    }
129
130    public Instant getBlocktime() {
131        return blocktime;
132    }
133
134    public static class VinList extends ArrayList<Vin> {
135    }
136
137    public static class VoutList extends ArrayList<Vout> {
138    }
139
140    public static class Vin {
141        public final Sha256Hash txid;
142        public final  long vout;
143        public final  Object scriptSig;
144        public final  long sequence;
145
146        @JsonCreator
147        public Vin(@JsonProperty("txid") Sha256Hash txid,
148                   @JsonProperty("vout") long vout,
149                   @JsonProperty("scriptSig") Object scriptSig,
150                   @JsonProperty("sequence") long sequence) {
151            this.txid = txid;
152            this.vout = vout;
153            this.scriptSig = scriptSig;
154            this.sequence = sequence;
155        }
156
157        public Sha256Hash getTxid() {
158            return txid;
159        }
160
161        public long getVout() {
162            return vout;
163        }
164
165        public Object getScriptSig() {
166            return scriptSig;
167        }
168
169        public long getSequence() {
170            return sequence;
171        }
172    }
173
174    public static class Vout {
175        public final  Coin value;
176        public final  int n;
177        public final  ScriptPubKeyInfo scriptPubKey;
178
179        @JsonCreator
180        public Vout(@JsonProperty("value")          Coin value,
181                    @JsonProperty("n")              int n,
182                    @JsonProperty("scriptPubKey")   ScriptPubKeyInfo scriptPubKey) {
183            this.value = value;
184            this.n = n;
185            this.scriptPubKey = scriptPubKey;
186        }
187
188        public Coin getValue() {
189            return value;
190        }
191
192        public int getN() {
193            return n;
194        }
195
196        public ScriptPubKeyInfo getScriptPubKey() {
197            return scriptPubKey;
198        }
199    }
200
201    public static class ScriptPubKeyInfo {
202        private final String asm;
203        private final String hex;
204        private final int reqSigs;
205        private final String type;
206        private final List<Address> addresses;
207
208        @JsonCreator
209        public ScriptPubKeyInfo(@JsonProperty("asm") String asm,
210                                @JsonProperty("hex") String hex,
211                                @JsonProperty("reqSigs") int reqSigs,
212                                @JsonProperty("type") String type,
213                                @JsonProperty("addresses") List<Address> addresses) {
214            this.asm = asm;
215            this.hex = hex;
216            this.reqSigs = reqSigs;
217            this.type = type;
218            this.addresses = addresses;
219        }
220
221        public ScriptPubKeyInfo(Script script) {
222            this.asm = script.toString();
223            this.hex = HexUtil.bytesToHexString(script.program());
224            this.reqSigs = -1;
225            this.type = "bitcoinj Script (not-fully-supported)";
226            this.addresses = List.of();
227        }
228
229        public String getAsm() {
230            return asm;
231        }
232
233        public String getHex() {
234            return hex;
235        }
236
237        public int getReqSigs() {
238            return reqSigs;
239        }
240
241        public String getType() {
242            return type;
243        }
244
245        public List<Address> getAddresses() {
246            return addresses;
247        }
248    }
249}