package com.greenaddress.greenapi;
import com.blockstream.libwally.Wally;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.MessageSerializer;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.ProtocolException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.core.Utils;
import org.bitcoinj.core.VarInt;
import java.io.IOException;
import java.io.OutputStream;
import static com.google.common.base.Preconditions.checkNotNull;
public class ElementsTransactionOutput extends TransactionOutput {
private long unblindedValue;
private byte[] assetId;
private byte[] assetTag;
private byte[] commitment;
private byte[] abf;
private byte[] vbf;
private byte[] blindingPubKey;
public void setAbfVbf(final byte[] newAbf, final byte[] newVbf, final byte[] assetId) {
if (newAbf != null) {
abf = newAbf;
assetTag = Wally.asset_generator_from_bytes(assetId, abf);
}
vbf = newVbf;
commitment = Wally.asset_value_commitment(unblindedValue, vbf, assetTag);
}
public long getUnblindedValue() {
return unblindedValue;
}
public byte[] getAbf() {
return abf;
}
public byte[] getVbf() {
return vbf;
}
public byte[] getAssetId() {
return assetId;
}
public byte[] getBlindingPubKey() {
return blindingPubKey;
}
public byte[] getAssetTag() {
return assetTag;
}
public void setUnblindedAssetTagFromAssetId(final byte[] assetId) {
assetTag = new byte[33];
assetTag[0] = 1;
System.arraycopy(assetId, 0, assetTag, 1, 32);
this.assetId = assetId;
}
public byte[] getCommitment() {
return commitment;
}
public ElementsTransactionOutput(final NetworkParameters params, final Transaction parent, final byte[] payload, final int offset, final MessageSerializer serializer) throws ProtocolException {
super(params, parent, payload, offset, serializer);
}
public ElementsTransactionOutput(final NetworkParameters params, final Transaction parent, final Coin value, final ConfidentialAddress to) {
super(params, parent, value, to.getBitcoinAddress());
}
public ElementsTransactionOutput(final NetworkParameters params, final Transaction parent, final Coin value) {
super(params, parent, value, new byte[0]);
this.abf = new byte[32];
this.vbf = new byte[32];
length += 1 + 33; // commitment prefix + tag
}
public ElementsTransactionOutput(final NetworkParameters params, final Transaction parent, final byte[] assetId, final Coin value, final ConfidentialAddress to) {
this(params, parent, Coin.ZERO, to);
blindingPubKey = to.getBlindingPubKey();
unblindedValue = value.getValue();
this.assetId = assetId;
setAbfVbf(CryptoHelper.randomBytes(32), CryptoHelper.randomBytes(32), assetId);
length += 2*33 - 8; // remove value (8), add tag/commitment (2*33)
}
@Override
protected void parse() throws ProtocolException {
assetTag = readBytes(33);
final byte first = readBytes(1)[0];
if (first == 1) {
value = (payload[cursor + 7] & 0xffL) |
((payload[cursor + 6] & 0xffL) << 8) |
((payload[cursor + 5] & 0xffL) << 16) |
((payload[cursor + 4] & 0xffL) << 24) |
((payload[cursor + 3] & 0xffL) << 32) |
((payload[cursor + 2] & 0xffL) << 40) |
((payload[cursor + 1] & 0xffL) << 48) |
((payload[cursor] & 0xffL) << 56);
cursor += 8;
} else {
final byte[] commitmentSuffix = readBytes(32);
commitment = new byte[33];
commitment[0] = first;
System.arraycopy(commitmentSuffix, 0, commitment, 1, 32);
}
scriptLen = (int) readVarInt();
length = cursor - offset + scriptLen;
scriptBytes = readBytes(scriptLen);
}
protected void bitcoinSerializeToStream(final OutputStream stream) throws IOException {
checkNotNull(getScriptBytes());
stream.write(assetTag);
if (value != 0) {
stream.write(new byte[] { 1 });
final byte[] out = new byte[8];
Utils.uint64ToByteArrayLE(value, out, 0);
for (int i = 0; i < 4; ++i) {
final byte tmp = out[i];
out[i] = out[7-i];
out[7-i] = tmp;
}
stream.write(out);
} else {
stream.write(commitment);
}
// TODO: Move script serialization into the Script class, where it belongs.
stream.write(new VarInt(getScriptBytes().length).encode());
stream.write(getScriptBytes());
}
public void setUnblindedValue(final long value) {
unblindedValue = value;
commitment = Wally.asset_value_commitment(unblindedValue, vbf, assetTag);
}
}