mirror of
https://github.com/sparrowwallet/drongo.git
synced 2025-11-05 11:56:38 +00:00
add pay to anchor script and address type
This commit is contained in:
parent
3b36947419
commit
abb598d3b0
5 changed files with 167 additions and 5 deletions
|
|
@ -134,6 +134,8 @@ public abstract class Address {
|
||||||
byte[] witnessProgram = Bech32.convertBits(convertedProgram, 0, convertedProgram.length, 5, 8, false);
|
byte[] witnessProgram = Bech32.convertBits(convertedProgram, 0, convertedProgram.length, 5, 8, false);
|
||||||
if(witnessProgram.length == 32) {
|
if(witnessProgram.length == 32) {
|
||||||
return new P2TRAddress(witnessProgram);
|
return new P2TRAddress(witnessProgram);
|
||||||
|
} else if(Arrays.equals(witnessProgram, ScriptType.ANCHOR_WITNESS_PROGRAM)) {
|
||||||
|
return new P2AAddress(witnessProgram);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.sparrowwallet.drongo.address;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.Network;
|
||||||
|
import com.sparrowwallet.drongo.protocol.Bech32;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
|
||||||
|
public class P2AAddress extends Address {
|
||||||
|
public P2AAddress(byte[] data) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion(Network network) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAddress(Network network) {
|
||||||
|
return Bech32.encode(network.getBech32AddressHRP(), getVersion(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScriptType getScriptType() {
|
||||||
|
return ScriptType.P2A;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOutputScriptDataType() {
|
||||||
|
return "Anchor";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -212,6 +212,10 @@ public class Script {
|
||||||
return addresses.toArray(new Address[addresses.size()]);
|
return addresses.toArray(new Address[addresses.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(P2A.isScriptType(this)) {
|
||||||
|
return new Address[] { P2A.getAddress(P2A.getDataFromScript(this)) };
|
||||||
|
}
|
||||||
|
|
||||||
throw new NonStandardScriptException("Cannot find addresses in non standard script: " + toString());
|
throw new NonStandardScriptException("Cannot find addresses in non standard script: " + toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1134,6 +1134,122 @@ public enum ScriptType {
|
||||||
public List<PolicyType> getAllowedPolicyTypes() {
|
public List<PolicyType> getAllowedPolicyTypes() {
|
||||||
return List.of(SINGLE);
|
return List.of(SINGLE);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
P2A("P2A", "Anchor (P2A)", "m/86'/0'/0'") {
|
||||||
|
@Override
|
||||||
|
public Address getAddress(byte[] data) {
|
||||||
|
return new P2AAddress(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address getAddress(ECKey derivedKey) {
|
||||||
|
throw new ProtocolException("Cannot create a anchor address with a key");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Address getAddress(Script script) {
|
||||||
|
throw new ProtocolException("Cannot create a anchor address with a script");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Script getOutputScript(byte[] data) {
|
||||||
|
List<ScriptChunk> chunks = new ArrayList<>();
|
||||||
|
chunks.add(new ScriptChunk(OP_1, null));
|
||||||
|
chunks.add(new ScriptChunk(data.length, data));
|
||||||
|
|
||||||
|
return new Script(chunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Script getOutputScript(ECKey derivedKey) {
|
||||||
|
throw new ProtocolException("Cannot create an anchor output script with a key");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Script getOutputScript(Script script) {
|
||||||
|
throw new ProtocolException("Cannot create an anchor output script with a script");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOutputDescriptor(ECKey derivedKey) {
|
||||||
|
throw new ProtocolException("Cannot create an anchor output descriptor with a key");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOutputDescriptor(Script script) {
|
||||||
|
throw new ProtocolException("Cannot create an anchor output descriptor with a script");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescriptor() {
|
||||||
|
return "addr(";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isScriptType(Script script) {
|
||||||
|
List<ScriptChunk> chunks = script.chunks;
|
||||||
|
if (chunks.size() != 2)
|
||||||
|
return false;
|
||||||
|
if (!chunks.get(0).equalsOpCode(OP_1))
|
||||||
|
return false;
|
||||||
|
byte[] chunk1data = chunks.get(1).data;
|
||||||
|
if (chunk1data == null)
|
||||||
|
return false;
|
||||||
|
if (!Arrays.equals(chunk1data, ANCHOR_WITNESS_PROGRAM)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getDataFromScript(Script script) {
|
||||||
|
return script.chunks.get(1).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getHashFromScript(Script script) {
|
||||||
|
throw new ProtocolException("P2A does not contain a hash");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ECKey getPublicKeyFromScript(Script script) {
|
||||||
|
throw new ProtocolException("P2A does not contain a key");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Script getScriptSig(Script scriptPubKey, ECKey pubKey, TransactionSignature signature) {
|
||||||
|
if(!isScriptType(scriptPubKey)) {
|
||||||
|
throw new ProtocolException("Provided scriptPubKey is not a " + getName() + " script");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Script(new byte[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransactionInput addSpendingInput(Transaction transaction, TransactionOutput prevOutput, ECKey pubKey, TransactionSignature signature) {
|
||||||
|
Script scriptSig = getScriptSig(prevOutput.getScript(), pubKey, signature);
|
||||||
|
return transaction.addInput(prevOutput.getHash(), prevOutput.getIndex(), scriptSig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Script getMultisigScriptSig(Script scriptPubKey, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
|
||||||
|
throw new UnsupportedOperationException("Constructing Taproot inputs is not yet supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransactionInput addMultisigSpendingInput(Transaction transaction, TransactionOutput prevOutput, int threshold, Map<ECKey, TransactionSignature> pubKeySignatures) {
|
||||||
|
throw new UnsupportedOperationException("Constructing Taproot inputs is not yet supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransactionSignature.Type getSignatureType() {
|
||||||
|
return TransactionSignature.Type.SCHNORR;
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PolicyType> getAllowedPolicyTypes() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
@ -1250,6 +1366,10 @@ public enum ScriptType {
|
||||||
|
|
||||||
public abstract boolean isScriptType(Script script);
|
public abstract boolean isScriptType(Script script);
|
||||||
|
|
||||||
|
public byte[] getDataFromScript(Script script) {
|
||||||
|
throw new ProtocolException("Script type " + this + " does not contain data");
|
||||||
|
}
|
||||||
|
|
||||||
public abstract byte[] getHashFromScript(Script script);
|
public abstract byte[] getHashFromScript(Script script);
|
||||||
|
|
||||||
public Address[] getAddresses(Script script) {
|
public Address[] getAddresses(Script script) {
|
||||||
|
|
@ -1282,11 +1402,13 @@ public enum ScriptType {
|
||||||
|
|
||||||
public static final ScriptType[] SINGLE_HASH_TYPES = {P2PKH, P2SH, P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH};
|
public static final ScriptType[] SINGLE_HASH_TYPES = {P2PKH, P2SH, P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH};
|
||||||
|
|
||||||
public static final ScriptType[] ADDRESSABLE_TYPES = {P2PKH, P2SH, P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH, P2TR};
|
public static final ScriptType[] ADDRESSABLE_TYPES = {P2PKH, P2SH, P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH, P2TR, P2A};
|
||||||
|
|
||||||
public static final ScriptType[] NON_WITNESS_TYPES = {P2PK, P2PKH, P2SH};
|
public static final ScriptType[] NON_WITNESS_TYPES = {P2PK, P2PKH, P2SH};
|
||||||
|
|
||||||
public static final ScriptType[] WITNESS_TYPES = {P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH, P2TR};
|
public static final ScriptType[] WITNESS_TYPES = {P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH, P2TR, P2A};
|
||||||
|
|
||||||
|
public static final byte[] ANCHOR_WITNESS_PROGRAM = new byte[] {78, 115};
|
||||||
|
|
||||||
public static List<ScriptType> getScriptTypesForPolicyType(PolicyType policyType) {
|
public static List<ScriptType> getScriptTypesForPolicyType(PolicyType policyType) {
|
||||||
return Arrays.stream(values()).filter(scriptType -> scriptType.isAllowed(policyType)).collect(Collectors.toList());
|
return Arrays.stream(values()).filter(scriptType -> scriptType.isAllowed(policyType)).collect(Collectors.toList());
|
||||||
|
|
@ -1364,6 +1486,8 @@ public enum ScriptType {
|
||||||
} else if(P2TR.equals(this)) {
|
} else if(P2TR.equals(this)) {
|
||||||
//Assume a default keypath spend
|
//Assume a default keypath spend
|
||||||
return (32 + 4 + 1 + ((double)66 / WITNESS_SCALE_FACTOR) + 4);
|
return (32 + 4 + 1 + ((double)66 / WITNESS_SCALE_FACTOR) + 4);
|
||||||
|
} else if(P2A.equals(this)) {
|
||||||
|
return 32 + 4 + 1 + 4;
|
||||||
} else if(Arrays.asList(WITNESS_TYPES).contains(this)) {
|
} else if(Arrays.asList(WITNESS_TYPES).contains(this)) {
|
||||||
//Return length of spending input with 75% discount to script size
|
//Return length of spending input with 75% discount to script size
|
||||||
return (32 + 4 + 1 + ((double)107 / WITNESS_SCALE_FACTOR) + 4);
|
return (32 + 4 + 1 + ((double)107 / WITNESS_SCALE_FACTOR) + 4);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.sparrowwallet.drongo.wallet;
|
package com.sparrowwallet.drongo.wallet;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.address.Address;
|
import com.sparrowwallet.drongo.address.Address;
|
||||||
|
import com.sparrowwallet.drongo.address.P2AAddress;
|
||||||
|
|
||||||
public class Payment {
|
public class Payment {
|
||||||
private Address address;
|
private Address address;
|
||||||
|
|
@ -15,10 +16,10 @@ public class Payment {
|
||||||
|
|
||||||
public Payment(Address address, String label, long amount, boolean sendMax, Type type) {
|
public Payment(Address address, String label, long amount, boolean sendMax, Type type) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.label = label;
|
this.label = label == null && address instanceof P2AAddress ? address.getOutputScriptDataType() : label;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.sendMax = sendMax;
|
this.sendMax = sendMax;
|
||||||
this.type = type;
|
this.type = type == Type.DEFAULT && address instanceof P2AAddress ? Type.ANCHOR : type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Address getAddress() {
|
public Address getAddress() {
|
||||||
|
|
@ -62,6 +63,6 @@ public class Payment {
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
DEFAULT, WHIRLPOOL_FEE, FAKE_MIX, MIX;
|
DEFAULT, WHIRLPOOL_FEE, FAKE_MIX, MIX, ANCHOR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue