/*
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Java API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
*/
package com.btchip;
import com.btchip.utils.BufferUtils;
import com.btchip.utils.Dump;
import com.btchip.utils.VarintUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Vector;
public class BitcoinTransaction {
public class BitcoinInput {
private byte[] prevOut;
private byte[] script;
private byte[] sequence;
public BitcoinInput(ByteArrayInputStream data) throws BTChipException {
try {
prevOut = new byte[36];
data.read(prevOut);
long scriptSize = VarintUtils.read(data);
script = new byte[(int)scriptSize];
data.read(script);
sequence = new byte[4];
data.read(sequence);
}
catch(Exception e) {
throw new BTChipException("Invalid encoding", e);
}
}
public BitcoinInput() {
prevOut = new byte[0];
script = new byte[0];
sequence = new byte[0];
}
public void serialize(ByteArrayOutputStream output) throws BTChipException {
BufferUtils.writeBuffer(output, prevOut);
VarintUtils.write(output, script.length);
BufferUtils.writeBuffer(output, script);
BufferUtils.writeBuffer(output, sequence);
}
public byte[] serializeOutputs() throws BTChipException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
VarintUtils.write(output, outputs.size());
for (BitcoinOutput outputItem : outputs) {
outputItem.serialize(output);
}
return output.toByteArray();
}
public byte[] getPrevOut() {
return prevOut;
}
public byte[] getScript() {
return script;
}
public byte[] getSequence() {
return sequence;
}
public void setPrevOut(byte[] prevOut) {
this.prevOut = prevOut;
}
public void setScript(byte[] script) {
this.script = script;
}
public void setSequence(byte[] sequence) {
this.sequence = sequence;
}
public String toString() {
return String.format("Prevout %s\r\nScript %s\r\nSequence %s\r\n", Dump.dump(prevOut),
Dump.dump(script), Dump.dump(sequence));
}
}
public class BitcoinOutput {
private byte[] amount;
private byte[] script;
public BitcoinOutput(ByteArrayInputStream data) throws BTChipException {
try {
amount = new byte[8];
data.read(amount);
long scriptSize = VarintUtils.read(data);
script = new byte[(int)scriptSize];
data.read(script);
}
catch(Exception e) {
throw new BTChipException("Invalid encoding", e);
}
}
public BitcoinOutput() {
amount = new byte[0];
script = new byte[0];
}
public void serialize(ByteArrayOutputStream output) throws BTChipException {
BufferUtils.writeBuffer(output, amount);
VarintUtils.write(output, script.length);
BufferUtils.writeBuffer(output, script);
}
public byte[] getAmount() {
return amount;
}
public byte[] getScript() {
return script;
}
public void setAmount(byte[] amount) {
this.amount = amount;
}
public void setScript(byte[] script) {
this.script = script;
}
public String toString() {
return String.format("Amount %s\r\nScript %s\r\n", Dump.dump(amount), Dump.dump(script));
}
}
private byte[] version;
private Vector<BitcoinInput> inputs;
private Vector<BitcoinOutput> outputs;
private byte[] lockTime;
public static final byte DEFAULT_VERSION[] = { (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00 };
public static final byte DEFAULT_SEQUENCE[] = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
public BitcoinTransaction(ByteArrayInputStream data) throws BTChipException {
inputs = new Vector<BitcoinInput>();
outputs = new Vector<BitcoinOutput>();
try {
version = new byte[4];
data.read(version);
long numberItems = VarintUtils.read(data);
for (long i=0; i<numberItems; i++) {
inputs.add(new BitcoinInput(data));
}
numberItems = VarintUtils.read(data);
for (long i=0; i<numberItems; i++) {
outputs.add(new BitcoinOutput(data));
}
lockTime = new byte[4];
data.read(lockTime);
}
catch(Exception e) {
throw new BTChipException("Invalid encoding", e);
}
}
public BitcoinTransaction() {
version = new byte[0];
inputs = new Vector<BitcoinInput>();
outputs = new Vector<BitcoinOutput>();
lockTime = new byte[0];
}
public byte[] serialize(boolean skipOutputLockTime) throws BTChipException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
BufferUtils.writeBuffer(output, version);
VarintUtils.write(output, inputs.size());
for (BitcoinInput input : inputs) {
input.serialize(output);
}
if (!skipOutputLockTime) {
VarintUtils.write(output, outputs.size());
for (BitcoinOutput outputItem : outputs) {
outputItem.serialize(output);
}
BufferUtils.writeBuffer(output, lockTime);
}
return output.toByteArray();
}
public byte[] getVersion() {
return version;
}
public Vector<BitcoinInput> getInputs() {
return inputs;
}
public Vector<BitcoinOutput> getOutputs() {
return outputs;
}
public byte[] getLockTime() {
return lockTime;
}
public void setVersion(byte[] version) {
this.version = version;
}
public void addInput(BitcoinInput input) {
this.inputs.add(input);
}
public void addOutput(BitcoinOutput output) {
this.outputs.add(output);
}
public void setLockTime(byte[] lockTime) {
this.lockTime = lockTime;
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("Version ").append(Dump.dump(version)).append('\r').append('\n');
int index = 1;
for (BitcoinInput input : inputs) {
buffer.append("Input #").append(index).append('\r').append('\n');
buffer.append(input.toString());
index++;
}
index = 1;
for (BitcoinOutput output : outputs) {
buffer.append("Output #").append(index).append('\r').append('\n');
buffer.append(output.toString());
index++;
}
buffer.append("LockTime ").append(Dump.dump(lockTime)).append('\r').append('\n');
return buffer.toString();
}
}