/* ******************************************************************************* * 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(); } }