-
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: Add AssetLockPayload and AssetUnlockPayload
Signed-off-by: HashEngineering <[email protected]>
- Loading branch information
1 parent
f098b0b
commit 248a68c
Showing
4 changed files
with
342 additions
and
0 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
102 changes: 102 additions & 0 deletions
102
core/src/main/java/org/bitcoinj/evolution/AssetLockPayload.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,102 @@ | ||
/* | ||
* Copyright 2023 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 org.bitcoinj.core.NetworkParameters; | ||
import org.bitcoinj.core.ProtocolException; | ||
import org.bitcoinj.core.Transaction; | ||
import org.bitcoinj.core.TransactionOutput; | ||
import org.bitcoinj.core.VarInt; | ||
import org.json.JSONObject; | ||
|
||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import static org.bitcoinj.core.Transaction.Type.TRANSACTION_ASSET_LOCK; | ||
|
||
public class AssetLockPayload extends SpecialTxPayload { | ||
|
||
public static final int CURRENT_VERSION = 1; | ||
public static final Transaction.Type SPECIALTX_TYPE = TRANSACTION_ASSET_LOCK; | ||
private ArrayList<TransactionOutput> creditOutputs; | ||
|
||
public AssetLockPayload(NetworkParameters params, Transaction tx) { | ||
super(params, tx); | ||
} | ||
|
||
public AssetLockPayload(NetworkParameters params, List<TransactionOutput> creditOutputs) { | ||
this(params, CURRENT_VERSION, creditOutputs); | ||
} | ||
|
||
public AssetLockPayload(NetworkParameters params, int version, List<TransactionOutput> creditOutputs) { | ||
super(params, version); | ||
this.creditOutputs = new ArrayList<>(creditOutputs); | ||
length = new VarInt(creditOutputs.size()).getSizeInBytes(); | ||
creditOutputs.forEach(output -> length += output.getMessageSize()); | ||
} | ||
@Override | ||
protected void parse() throws ProtocolException { | ||
super.parse(); | ||
int size = (int) readVarInt(); | ||
creditOutputs = Lists.newArrayList(); | ||
for (int i = 0; i < size; ++i) { | ||
TransactionOutput output = new TransactionOutput(params, null, payload, cursor); | ||
cursor += output.getMessageSize(); | ||
creditOutputs.add(output); | ||
} | ||
length = cursor - offset; | ||
} | ||
|
||
@Override | ||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { | ||
super.bitcoinSerializeToStream(stream); | ||
stream.write(new VarInt(creditOutputs.size()).encode()); | ||
for (int i = 0; i < creditOutputs.size(); ++i) { | ||
creditOutputs.get(i).bitcoinSerialize(stream); | ||
} | ||
} | ||
|
||
public int getCurrentVersion() { | ||
return CURRENT_VERSION; | ||
} | ||
|
||
public String toString() { | ||
return String.format("AssetLockPayload(creditOutputs: %d)", | ||
creditOutputs.size()); | ||
} | ||
|
||
@Override | ||
public Transaction.Type getType() { | ||
return Transaction.Type.TRANSACTION_ASSET_UNLOCK; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "AssetLock"; | ||
} | ||
|
||
@Override | ||
public JSONObject toJson() { | ||
JSONObject result = super.toJson(); | ||
creditOutputs.forEach(output -> result.append("creditOutputs", output.toString())); | ||
return result; | ||
} | ||
} |
110 changes: 110 additions & 0 deletions
110
core/src/main/java/org/bitcoinj/evolution/AssetUnlockPayload.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,110 @@ | ||
/* | ||
* Copyright 2023 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 org.bitcoinj.core.NetworkParameters; | ||
import org.bitcoinj.core.ProtocolException; | ||
import org.bitcoinj.core.Sha256Hash; | ||
import org.bitcoinj.core.Transaction; | ||
import org.bitcoinj.core.Utils; | ||
import org.bitcoinj.crypto.BLSLazySignature; | ||
import org.json.JSONObject; | ||
|
||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.math.BigInteger; | ||
|
||
import static org.bitcoinj.core.Transaction.Type.TRANSACTION_ASSET_UNLOCK; | ||
|
||
public class AssetUnlockPayload extends SpecialTxPayload { | ||
|
||
public static final int CURRENT_VERSION = 1; | ||
public static final Transaction.Type SPECIALTX_TYPE = TRANSACTION_ASSET_UNLOCK; | ||
|
||
private long index; | ||
private long fee; | ||
private long requestedHeight; | ||
private Sha256Hash quorumHash; | ||
BLSLazySignature quorumSig; | ||
public AssetUnlockPayload(NetworkParameters params, Transaction tx) { | ||
super(params, tx); | ||
} | ||
|
||
|
||
public AssetUnlockPayload(NetworkParameters params, int version, long index, long fee, int requestedHeight, Sha256Hash quorumHash, BLSLazySignature quorumSig) { | ||
super(params, version); | ||
this.index = index; | ||
this.fee = fee; | ||
this.requestedHeight = requestedHeight; | ||
this.quorumHash = quorumHash; | ||
this.quorumSig = quorumSig; | ||
length = 8 + 4 + 4 + 32 + quorumSig.getMessageSize(); | ||
} | ||
|
||
@Override | ||
protected void parse() throws ProtocolException { | ||
super.parse(); | ||
index = readInt64(); | ||
fee = readUint32(); | ||
requestedHeight = readUint32(); | ||
quorumHash = readHash(); | ||
quorumSig = new BLSLazySignature(params, payload, cursor); | ||
cursor += quorumSig.getMessageSize(); | ||
length = cursor - offset; | ||
} | ||
|
||
@Override | ||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { | ||
super.bitcoinSerializeToStream(stream); | ||
Utils.uint64ToByteStreamLE(BigInteger.valueOf(index), stream); | ||
Utils.uint32ToByteStreamLE(fee, stream); | ||
Utils.uint32ToByteStreamLE(requestedHeight, stream); | ||
stream.write(quorumHash.getReversedBytes()); | ||
quorumSig.bitcoinSerialize(stream); | ||
} | ||
|
||
public int getCurrentVersion() { | ||
return CURRENT_VERSION; | ||
} | ||
|
||
public String toString() { | ||
return String.format("AssetUnlockPayload(index: %d, fee: %d, requestedHeight: %d, quorumHash: %s, quorumSig: %s)", | ||
index, fee, requestedHeight, quorumHash, quorumSig); | ||
} | ||
|
||
@Override | ||
public Transaction.Type getType() { | ||
return TRANSACTION_ASSET_UNLOCK; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "AssetUnlock"; | ||
} | ||
|
||
@Override | ||
public JSONObject toJson() { | ||
JSONObject result = super.toJson(); | ||
result.put("index", index); | ||
result.put("fee", fee); | ||
result.put("requestedHeight", requestedHeight); | ||
result.put("quorumHash", quorumHash); | ||
result.put("quorumSig", quorumSig); | ||
return result; | ||
} | ||
} |
118 changes: 118 additions & 0 deletions
118
core/src/test/java/org/bitcoinj/evolution/AssetLockTest.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,118 @@ | ||
package org.bitcoinj.evolution; | ||
|
||
import com.google.common.collect.Lists; | ||
import org.bitcoinj.core.ECKey; | ||
import org.bitcoinj.core.Sha256Hash; | ||
import org.bitcoinj.core.Transaction; | ||
import org.bitcoinj.core.TransactionOutput; | ||
import org.bitcoinj.crypto.BLSLazySignature; | ||
import org.bitcoinj.params.UnitTestParams; | ||
import org.bitcoinj.script.Script; | ||
import org.bitcoinj.script.ScriptBuilder; | ||
import org.bitcoinj.wallet.DefaultRiskAnalysis; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
import java.util.ArrayList; | ||
|
||
import static org.bitcoinj.core.Coin.CENT; | ||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertSame; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
public class AssetLockTest { | ||
UnitTestParams PARAMS = UnitTestParams.get(); | ||
ArrayList<Transaction> dummyTransactions; | ||
ArrayList<TransactionOutput> dummyOutputs; | ||
|
||
@Before | ||
public void setupDummyInputs() | ||
{ | ||
dummyTransactions = Lists.newArrayListWithCapacity(2); | ||
dummyTransactions.add(new Transaction(PARAMS)); | ||
dummyTransactions.add(new Transaction(PARAMS)); | ||
dummyOutputs = Lists.newArrayListWithCapacity(4); | ||
// Add some keys to the keystore: | ||
ECKey [] key = new ECKey[] { | ||
new ECKey(), new ECKey(), new ECKey(), new ECKey() | ||
}; | ||
|
||
dummyTransactions.get(0).addOutput(CENT.multiply(11), ScriptBuilder.createP2PKOutputScript(key[0])); | ||
dummyTransactions.get(0).addOutput(CENT.multiply(50), ScriptBuilder.createP2PKOutputScript(key[1])); | ||
dummyOutputs.addAll(dummyTransactions.get(0).getOutputs()); | ||
|
||
|
||
dummyTransactions.get(0).addOutput(CENT.multiply(21), ScriptBuilder.createP2PKOutputScript(key[2])); | ||
dummyTransactions.get(0).addOutput(CENT.multiply(22), ScriptBuilder.createP2PKOutputScript(key[3])); | ||
dummyOutputs.addAll(dummyTransactions.get(1).getOutputs()); | ||
} | ||
|
||
private Transaction createAssetLockTx(ECKey key) { | ||
|
||
ArrayList<TransactionOutput> creditOutputs = Lists.newArrayListWithCapacity(2); | ||
creditOutputs.add(new TransactionOutput(PARAMS, null, CENT.multiply(17), ScriptBuilder.createP2PKOutputScript(key).getProgram())); | ||
creditOutputs.add(new TransactionOutput(PARAMS, null, CENT.multiply(13), ScriptBuilder.createP2PKOutputScript(key).getProgram())); | ||
|
||
AssetLockPayload assetLockTx = new AssetLockPayload(PARAMS, 1, creditOutputs); | ||
|
||
Transaction tx = new Transaction(PARAMS); | ||
tx.setVersionAndType(3, Transaction.Type.TRANSACTION_ASSET_LOCK); | ||
tx.setExtraPayload(assetLockTx); | ||
tx.addInput(dummyTransactions.get(0).getTxId(), 1, new Script(new byte[65])); | ||
tx.addOutput(CENT.multiply(30), ScriptBuilder.createOpReturnScript(new byte[0])); | ||
tx.addOutput(CENT.multiply(20), ScriptBuilder.createP2PKOutputScript(key)); | ||
return tx; | ||
} | ||
|
||
private Transaction createAssetUnlockTx(ECKey key) | ||
{ | ||
int version = 1; | ||
// just a big number bigger than uint32_t | ||
long index = 0x001122334455667788L; | ||
// big enough to overflow int32_t | ||
int fee = 2000000000; | ||
// just big enough to overflow uint16_t | ||
int requestedHeight = 1000000; | ||
Sha256Hash quorumHash = Sha256Hash.ZERO_HASH; | ||
BLSLazySignature quorumSig = new BLSLazySignature(PARAMS); | ||
AssetUnlockPayload assetUnlockTx = new AssetUnlockPayload(PARAMS, version, index, fee, requestedHeight, quorumHash, quorumSig); | ||
|
||
Transaction tx = new Transaction(PARAMS); | ||
tx.setVersionAndType(3, Transaction.Type.TRANSACTION_ASSET_UNLOCK); | ||
tx.setExtraPayload(assetUnlockTx); | ||
|
||
tx.addOutput(CENT.multiply(10), ScriptBuilder.createP2PKOutputScript(key)); | ||
tx.addOutput(CENT.multiply(20), ScriptBuilder.createP2PKOutputScript(key)); | ||
|
||
return tx; | ||
} | ||
|
||
@Test | ||
public void assetLock() { | ||
ECKey key = new ECKey(); | ||
|
||
Transaction tx = createAssetLockTx(key); | ||
assertSame(DefaultRiskAnalysis.RuleViolation.NONE, DefaultRiskAnalysis.isStandard(tx)); | ||
|
||
tx.verify(); | ||
assertTrue(tx.getInputs().stream().allMatch(input -> DefaultRiskAnalysis.isInputStandard(input) == DefaultRiskAnalysis.RuleViolation.NONE)); | ||
|
||
// Check version | ||
assertEquals(3, tx.getVersionShort()); | ||
AssetLockPayload lockPayload = (AssetLockPayload) tx.getExtraPayloadObject(); | ||
assertEquals(1, lockPayload.getVersion()); | ||
} | ||
|
||
@Test | ||
public void assetUnlock() { | ||
ECKey key = new ECKey(); | ||
final Transaction tx = createAssetUnlockTx(key); | ||
assertSame(DefaultRiskAnalysis.RuleViolation.NONE, DefaultRiskAnalysis.isStandard(tx)); | ||
tx.verify(); | ||
|
||
// Check version | ||
assertEquals(3, tx.getVersionShort()); | ||
AssetLockPayload lockPayload = (AssetLockPayload) tx.getExtraPayloadObject(); | ||
assertEquals(1, lockPayload.getVersion()); | ||
} | ||
} |