use of com.sparrowwallet.drongo.psbt.PSBTEntry in project drongo by sparrowwallet.
the class PSBT method parseGlobalEntries.
private void parseGlobalEntries(List<PSBTEntry> globalEntries) throws PSBTParseException {
PSBTEntry duplicate = findDuplicateKey(globalEntries);
if (duplicate != null) {
throw new PSBTParseException("Found duplicate key for PSBT global: " + Utils.bytesToHex(duplicate.getKey()));
}
for (PSBTEntry entry : globalEntries) {
switch(entry.getKeyType()) {
case PSBT_GLOBAL_UNSIGNED_TX:
entry.checkOneByteKey();
Transaction transaction = new Transaction(entry.getData());
transaction.verify();
inputs = transaction.getInputs().size();
outputs = transaction.getOutputs().size();
log.debug("Transaction with txid: " + transaction.getTxId() + " version " + transaction.getVersion() + " size " + transaction.getMessageSize() + " locktime " + transaction.getLocktime());
for (TransactionInput input : transaction.getInputs()) {
if (input.getScriptSig().getProgram().length != 0) {
throw new PSBTParseException("Unsigned tx input does not have empty scriptSig");
}
log.debug(" Transaction input references txid: " + input.getOutpoint().getHash() + " vout " + input.getOutpoint().getIndex() + " with script " + input.getScriptSig());
}
for (TransactionOutput output : transaction.getOutputs()) {
try {
log.debug(" Transaction output value: " + output.getValue() + " to addresses " + Arrays.asList(output.getScript().getToAddresses()) + " with script hex " + Utils.bytesToHex(output.getScript().getProgram()) + " to script " + output.getScript());
} catch (NonStandardScriptException e) {
log.debug(" Transaction output value: " + output.getValue() + " with script hex " + Utils.bytesToHex(output.getScript().getProgram()) + " to script " + output.getScript());
}
}
this.transaction = transaction;
break;
case PSBT_GLOBAL_BIP32_PUBKEY:
entry.checkOneBytePlusXpubKey();
KeyDerivation keyDerivation = parseKeyDerivation(entry.getData());
ExtendedKey pubKey = ExtendedKey.fromDescriptor(Base58.encodeChecked(entry.getKeyData()));
this.extendedPublicKeys.put(pubKey, keyDerivation);
log.debug("Pubkey with master fingerprint " + keyDerivation.getMasterFingerprint() + " at path " + keyDerivation.getDerivationPath() + ": " + pubKey.getExtendedKey());
break;
case PSBT_GLOBAL_VERSION:
entry.checkOneByteKey();
int version = (int) Utils.readUint32(entry.getData(), 0);
this.version = version;
log.debug("PSBT version: " + version);
break;
case PSBT_GLOBAL_PROPRIETARY:
globalProprietary.put(Utils.bytesToHex(entry.getKeyData()), Utils.bytesToHex(entry.getData()));
log.debug("PSBT global proprietary data: " + Utils.bytesToHex(entry.getData()));
break;
default:
log.warn("PSBT global not recognized key type: " + entry.getKeyType());
}
}
}
use of com.sparrowwallet.drongo.psbt.PSBTEntry in project drongo by sparrowwallet.
the class PSBT method parseOutputEntries.
private void parseOutputEntries(List<List<PSBTEntry>> outputEntryLists) throws PSBTParseException {
for (List<PSBTEntry> outputEntries : outputEntryLists) {
PSBTEntry duplicate = findDuplicateKey(outputEntries);
if (duplicate != null) {
throw new PSBTParseException("Found duplicate key for PSBT output: " + Utils.bytesToHex(duplicate.getKey()));
}
PSBTOutput output = new PSBTOutput(outputEntries);
this.psbtOutputs.add(output);
}
}
use of com.sparrowwallet.drongo.psbt.PSBTEntry in project drongo by sparrowwallet.
the class PSBT method parseInputEntries.
private void parseInputEntries(List<List<PSBTEntry>> inputEntryLists, boolean verifySignatures) throws PSBTParseException {
for (List<PSBTEntry> inputEntries : inputEntryLists) {
PSBTEntry duplicate = findDuplicateKey(inputEntries);
if (duplicate != null) {
throw new PSBTParseException("Found duplicate key for PSBT input: " + Utils.bytesToHex(duplicate.getKey()));
}
int inputIndex = this.psbtInputs.size();
PSBTInput input = new PSBTInput(this, inputEntries, transaction, inputIndex);
this.psbtInputs.add(input);
}
if (verifySignatures) {
for (PSBTInput input : psbtInputs) {
boolean verified = input.verifySignatures();
if (!verified && input.getPartialSignatures().size() > 0) {
throw new PSBTSignatureException("Unverifiable partial signatures provided");
}
}
}
}
use of com.sparrowwallet.drongo.psbt.PSBTEntry in project drongo by sparrowwallet.
the class PSBT method serialize.
public byte[] serialize(boolean includeXpubs) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.writeBytes(Utils.hexToBytes(PSBT_MAGIC_HEX));
baos.writeBytes(new byte[] { (byte) 0xff });
List<PSBTEntry> globalEntries = getGlobalEntries();
for (PSBTEntry entry : globalEntries) {
if (includeXpubs || (entry.getKeyType() != PSBT_GLOBAL_BIP32_PUBKEY && entry.getKeyType() != PSBT_GLOBAL_PROPRIETARY)) {
entry.serializeToStream(baos);
}
}
baos.writeBytes(new byte[] { (byte) 0x00 });
for (PSBTInput psbtInput : getPsbtInputs()) {
List<PSBTEntry> inputEntries = psbtInput.getInputEntries();
for (PSBTEntry entry : inputEntries) {
if (includeXpubs || (entry.getKeyType() != PSBT_IN_BIP32_DERIVATION && entry.getKeyType() != PSBT_IN_PROPRIETARY && entry.getKeyType() != PSBT_IN_TAP_INTERNAL_KEY && entry.getKeyType() != PSBT_IN_TAP_BIP32_DERIVATION)) {
entry.serializeToStream(baos);
}
}
baos.writeBytes(new byte[] { (byte) 0x00 });
}
for (PSBTOutput psbtOutput : getPsbtOutputs()) {
List<PSBTEntry> outputEntries = psbtOutput.getOutputEntries();
for (PSBTEntry entry : outputEntries) {
if (includeXpubs || (entry.getKeyType() != PSBT_OUT_REDEEM_SCRIPT && entry.getKeyType() != PSBT_OUT_WITNESS_SCRIPT && entry.getKeyType() != PSBT_OUT_BIP32_DERIVATION && entry.getKeyType() != PSBT_OUT_PROPRIETARY && entry.getKeyType() != PSBT_OUT_TAP_INTERNAL_KEY && entry.getKeyType() != PSBT_OUT_TAP_BIP32_DERIVATION)) {
entry.serializeToStream(baos);
}
}
baos.writeBytes(new byte[] { (byte) 0x00 });
}
return baos.toByteArray();
}
use of com.sparrowwallet.drongo.psbt.PSBTEntry in project drongo by sparrowwallet.
the class PSBT method parse.
private void parse(boolean verifySignatures) throws PSBTParseException {
int seenInputs = 0;
int seenOutputs = 0;
ByteBuffer psbtByteBuffer = ByteBuffer.wrap(psbtBytes);
byte[] magicBuf = new byte[4];
psbtByteBuffer.get(magicBuf);
if (!PSBT_MAGIC_HEX.equalsIgnoreCase(Utils.bytesToHex(magicBuf))) {
throw new PSBTParseException("PSBT has invalid magic value");
}
byte sep = psbtByteBuffer.get();
if (sep != (byte) 0xff) {
throw new PSBTParseException("PSBT has bad initial separator: " + Utils.bytesToHex(new byte[] { sep }));
}
int currentState = STATE_GLOBALS;
List<PSBTEntry> globalEntries = new ArrayList<>();
List<List<PSBTEntry>> inputEntryLists = new ArrayList<>();
List<List<PSBTEntry>> outputEntryLists = new ArrayList<>();
List<PSBTEntry> inputEntries = new ArrayList<>();
List<PSBTEntry> outputEntries = new ArrayList<>();
while (psbtByteBuffer.hasRemaining()) {
PSBTEntry entry = new PSBTEntry(psbtByteBuffer);
if (entry.getKey() == null) {
// length == 0
switch(currentState) {
case STATE_GLOBALS:
currentState = STATE_INPUTS;
parseGlobalEntries(globalEntries);
break;
case STATE_INPUTS:
inputEntryLists.add(inputEntries);
inputEntries = new ArrayList<>();
seenInputs++;
if (seenInputs == inputs) {
currentState = STATE_OUTPUTS;
parseInputEntries(inputEntryLists, verifySignatures);
}
break;
case STATE_OUTPUTS:
outputEntryLists.add(outputEntries);
outputEntries = new ArrayList<>();
seenOutputs++;
if (seenOutputs == outputs) {
currentState = STATE_END;
parseOutputEntries(outputEntryLists);
}
break;
case STATE_END:
break;
default:
throw new PSBTParseException("PSBT structure invalid");
}
} else if (currentState == STATE_GLOBALS) {
globalEntries.add(entry);
} else if (currentState == STATE_INPUTS) {
inputEntries.add(entry);
} else if (currentState == STATE_OUTPUTS) {
outputEntries.add(entry);
} else {
throw new PSBTParseException("PSBT structure invalid");
}
}
if (currentState != STATE_END) {
if (transaction == null) {
throw new PSBTParseException("Missing transaction");
}
}
if (log.isDebugEnabled()) {
log.debug("Calculated fee at " + getFee());
}
}
Aggregations