-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(coinjoin): improve asset lock handling and other fixes (#244)
* feat: improve asset lock handling to match v20 updates * refactor: rename credit funding to asset lock * tests: update tests * fix(coinjoin): add missing is spent check to countInputsWithAmount * feat(wallet-tool): add parameter to return change with coinjoin send * feat(coinjoin): add tx history at the end of a wallet dump * refactor: improve AssetLockTransaction for multiple credit outputs * refactor: rename setAssetLockPublicKey to addAssetLockPublicKey * tests: add more tests to AuthenticationGroupExtensionTest * fix: fix incorrect inequality check in createDenominate * fixes bug that prevented creation of more denominations past the goal count * feat: add txid to the transaction history report
- Loading branch information
1 parent
eb4e227
commit f6cd23d
Showing
20 changed files
with
753 additions
and
512 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
247 changes: 247 additions & 0 deletions
247
core/src/main/java/org/bitcoinj/evolution/AssetLockTransaction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
/* | ||
* Copyright 2020 Dash Core Group | ||
* | ||
* 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 org.bitcoinj.evolution; | ||
|
||
import com.google.common.collect.Lists; | ||
import com.google.common.collect.Maps; | ||
import org.bitcoinj.core.*; | ||
import org.bitcoinj.crypto.DeterministicKey; | ||
import org.bitcoinj.crypto.IDeterministicKey; | ||
import org.bitcoinj.script.Script; | ||
import org.bitcoinj.script.ScriptBuilder; | ||
import org.bitcoinj.script.ScriptPattern; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.TreeMap; | ||
|
||
import static com.google.common.base.Preconditions.checkState; | ||
|
||
/** | ||
* This class extends Transaction and is used to create a funding | ||
* transaction for an identity. It also can store other information | ||
* that is not stored in the blockchain transaction which includes | ||
* the public or private key's associated with this transaction. | ||
*/ | ||
public class AssetLockTransaction extends Transaction { | ||
|
||
private ArrayList<TransactionOutPoint> lockedOutpoints; | ||
private ArrayList<Sha256Hash> identityIds; | ||
private TreeMap<Integer, ECKey> assetLockPublicKeys; | ||
private ArrayList<KeyId> assetLockPublicKeyIds; | ||
private AssetLockPayload assetLockPayload; | ||
|
||
|
||
|
||
public AssetLockTransaction(NetworkParameters params) { | ||
super(params); | ||
} | ||
|
||
/** | ||
* Create an asset lock transaction from an existing transaction. | ||
* This should only be called if {@link AssetLockTransaction#isAssetLockTransaction(Transaction)} | ||
* returns true. | ||
* @param tx this transaction should be a credit funding transaction | ||
*/ | ||
public AssetLockTransaction(Transaction tx) { | ||
super(tx.getParams(), tx.bitcoinSerialize(), 0); | ||
} | ||
|
||
/** | ||
* Creates a credit funding transaction with a single credit output. | ||
* @param params | ||
* @param assetLockPublicKey The key from which the hash160 will be placed in the OP_RETURN output | ||
* @param fundingAmount The amount of dash that will be locked in the OP_RETURN output | ||
*/ | ||
public AssetLockTransaction(NetworkParameters params, ECKey assetLockPublicKey, Coin fundingAmount) { | ||
super(params); | ||
setVersionAndType(SPECIAL_VERSION, Type.TRANSACTION_ASSET_LOCK); | ||
this.assetLockPublicKeys = Maps.newTreeMap(); | ||
assetLockPublicKeys.put(0, assetLockPublicKey); | ||
this.assetLockPublicKeyIds = Lists.newArrayList(); | ||
assetLockPublicKeyIds.add(KeyId.fromBytes(assetLockPublicKey.getPubKeyHash())); | ||
this.identityIds = Lists.newArrayList(); | ||
identityIds.add(Sha256Hash.ZERO_HASH); | ||
|
||
TransactionOutput realOutput = new TransactionOutput(params, this, fundingAmount, Address.fromKey(params, assetLockPublicKey)); | ||
|
||
lockedOutpoints = Lists.newArrayList(); | ||
TransactionOutput assetLockOutput = new TransactionOutput(params, null, fundingAmount, ScriptBuilder.createAssetLockOutput().getProgram()); | ||
assetLockPayload = new AssetLockPayload(params, Lists.newArrayList(realOutput)); | ||
setExtraPayload(assetLockPayload); | ||
addOutput(assetLockOutput); | ||
} | ||
|
||
/** | ||
* Creates a credit funding transaction by reading payload. | ||
* Length of a transaction is fixed. | ||
*/ | ||
|
||
public AssetLockTransaction(NetworkParameters params, byte [] payload) { | ||
super(params, payload, 0); | ||
} | ||
|
||
/** | ||
* Deserialize and initialize some fields from the credit burn output | ||
*/ | ||
@Override | ||
protected void parse() throws ProtocolException { | ||
super.parse(); | ||
parseTransaction(); | ||
} | ||
|
||
@Override | ||
protected void unCache() { | ||
super.unCache(); | ||
lockedOutpoints.clear(); | ||
identityIds.clear(); | ||
assetLockPublicKeyIds.clear(); | ||
} | ||
|
||
/** | ||
* Initializes lockedOutpoints and the hash160 | ||
* assetlock key | ||
*/ | ||
private void parseTransaction() { | ||
assetLockPayload = (AssetLockPayload) getExtraPayloadObject(); | ||
lockedOutpoints = Lists.newArrayList(); | ||
assetLockPublicKeyIds = Lists.newArrayList(); | ||
assetLockPublicKeys = Maps.newTreeMap(); | ||
identityIds = Lists.newArrayList(); | ||
getLockedOutpoint(); | ||
getAssetLockPublicKeyId(); | ||
getIdentityId(); | ||
} | ||
|
||
/** | ||
* Sets lockedOutput and returns output that has the OP_RETURN script | ||
*/ | ||
|
||
public TransactionOutput getLockedOutput() { | ||
return getLockedOutput(0); | ||
} | ||
|
||
public TransactionOutput getLockedOutput(int outputIndex) { | ||
return assetLockPayload.getCreditOutputs().get(outputIndex); | ||
} | ||
|
||
public TransactionOutPoint getLockedOutpoint() { | ||
return getLockedOutpoint(0); | ||
} | ||
|
||
public AssetLockPayload getAssetLockPayload() { | ||
return assetLockPayload; | ||
} | ||
|
||
/** | ||
* Sets lockedOutpoint and returns outpoint that has the OP_RETURN script | ||
*/ | ||
|
||
|
||
|
||
public TransactionOutPoint getLockedOutpoint(int outputIndex) { | ||
if (lockedOutpoints.isEmpty()) { | ||
for (int i = 0; i < assetLockPayload.getCreditOutputs().size(); ++i) { | ||
lockedOutpoints.add(new TransactionOutPoint(params, i, Sha256Hash.wrap(getTxId().getReversedBytes()))); | ||
} | ||
} | ||
return lockedOutpoints.get(outputIndex); | ||
} | ||
|
||
public Coin getFundingAmount() { | ||
return assetLockPayload.getFundingAmount(); | ||
} | ||
|
||
/** | ||
* Returns the credit burn identifier, which is the sha256(sha256(outpoint)) | ||
*/ | ||
public Sha256Hash getIdentityId() { | ||
return getIdentityId(0); | ||
} | ||
|
||
public Sha256Hash getIdentityId(int outputIndex) { | ||
if(identityIds.isEmpty()) { | ||
assetLockPayload.getCreditOutputs().forEach(transactionOutput -> { | ||
try { | ||
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(36); | ||
getLockedOutpoint(outputIndex).bitcoinSerialize(bos); | ||
identityIds.add(Sha256Hash.twiceOf(bos.toByteArray())); | ||
|
||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}); | ||
} | ||
return identityIds.get(0); | ||
} | ||
|
||
public ECKey getAssetLockPublicKey() { | ||
return getAssetLockPublicKey(0); | ||
} | ||
|
||
public ECKey getAssetLockPublicKey(int outputIndex) { | ||
return assetLockPublicKeys.get(outputIndex); | ||
} | ||
|
||
|
||
public KeyId getAssetLockPublicKeyId() { | ||
return getAssetLockPublicKeyId(0); | ||
} | ||
public KeyId getAssetLockPublicKeyId(int outputIndex) { | ||
if(assetLockPublicKeyIds.isEmpty()) { | ||
assetLockPayload.getCreditOutputs().forEach(transactionOutput -> assetLockPublicKeyIds.add(KeyId.fromBytes(ScriptPattern.extractHashFromP2PKH(assetLockPayload.getCreditOutputs().get(0).getScriptPubKey())))); | ||
} | ||
return assetLockPublicKeyIds.get(outputIndex); | ||
} | ||
|
||
public int getUsedDerivationPathIndex(int outputIndex) { | ||
ECKey key = getAssetLockPublicKey(0); | ||
if (key instanceof IDeterministicKey) { | ||
IDeterministicKey deterministicKey = (IDeterministicKey) key; | ||
return deterministicKey.getPath().get(deterministicKey.getDepth() - 1).num(); | ||
} | ||
return -1; | ||
} | ||
|
||
public void addAssetLockPublicKey(ECKey assetLockPublicKey) { | ||
int index = assetLockPublicKeyIds.indexOf(KeyId.fromBytes(assetLockPublicKey.getPubKeyHash())); | ||
checkState(index != -1, "cannot find public key hash for " + assetLockPublicKey); | ||
assetLockPublicKeys.put(index, assetLockPublicKey); | ||
} | ||
|
||
/** | ||
* Determines if a transaction has one or more credit burn outputs | ||
* and therefore is a is credit funding transaction | ||
*/ | ||
public static boolean isAssetLockTransaction(Transaction tx) { | ||
return tx.getVersionShort() == SPECIAL_VERSION && tx.getType() == Type.TRANSACTION_ASSET_LOCK && | ||
tx.getOutputs().stream().anyMatch(output -> ScriptPattern.isAssetLock(output.getScriptPubKey())); | ||
} | ||
|
||
/** | ||
* Determines the first output that is a credit burn output | ||
* or returns -1. | ||
*/ | ||
public long getAssetLockOutputIndex() { | ||
int outputCount = getOutputs().size(); | ||
for (int i = 0; i < outputCount; ++i) { | ||
if (ScriptPattern.isAssetLock(getOutput(i).getScriptPubKey())) | ||
return i; | ||
} | ||
return -1; | ||
} | ||
} |
Oops, something went wrong.