Function isInvalidReason
Get result of transaction data and signature verification
string isInvalidReason
(
in ref const(Transaction) tx,
Engine engine,
scope nothrow @safe bool delegate(in ref geod24 .bitblob .BitBlob!(64L), out UTXO) findUTXO,
in const(Height) height,
scope nothrow @safe string delegate(in ref Transaction, Amount) checkFee,
scope nothrow @safe Amount delegate(geod24 .bitblob .BitBlob!(64L)) getPenaltyDeposit
) nothrow @safe;
Parameters
Name | Description |
---|---|
tx | Transaction |
engine | script execution engine |
findUTXO | delegate for finding Output |
height | height of block |
checkFee | delegate for checking tx fee |
Return
null
if the transaction is valid, a string explaining the reason it
is invalid otherwise.
Example
verify transaction data
scope engine = new Engine();
scope storage = new MemoryUTXOSet;
KeyPair[] key_pairs = [KeyPair .random, KeyPair .random, KeyPair .random, KeyPair .random];
scope payload_checker = new FeeManager();
scope checker = &payload_checker .check;
// Creates the first transaction.
Transaction previousTx = Transaction([ Output(Amount(100), key_pairs[0] .address) ]);
// Save
Hash previousHash = hashFull(previousTx);
storage .put(previousTx);
// Creates the second transaction.
Transaction secondTx = Transaction(
[
Input(previousHash, 0)
],
[
Output(Amount(50), key_pairs[1] .address)
]
);
secondTx .inputs[0] .unlock = signUnlock(key_pairs[0], secondTx);
// It is validated. (the sum of `Output` < the sum of `Input`)
assert(secondTx .isValid(engine, storage .getUTXOFinder(), Height(0), checker),
format("Transaction data is not validated %s", secondTx));
secondTx .outputs ~= Output(Amount(50), key_pairs[2] .address);
secondTx .outputs .sort;
secondTx .inputs[0] .unlock = signUnlock(key_pairs[0], secondTx);
// It is validated. (the sum of `Output` == the sum of `Input`)
assert(secondTx .isValid(engine, storage .getUTXOFinder(), Height(0), checker),
format("Transaction data is not validated %s", secondTx));
secondTx .outputs ~= Output(Amount(50), key_pairs[3] .address);
secondTx .outputs .sort;
secondTx .inputs[0] .unlock = signUnlock(key_pairs[0], secondTx);
// It isn't validated. (the sum of `Output` > the sum of `Input`)
assert(!secondTx .isValid(engine, storage .getUTXOFinder(), Height(0), checker),
format("Transaction data is not validated %s", secondTx));
Example
negative output amounts disallowed
scope engine = new Engine();
KeyPair[] key_pairs = [KeyPair .random(), KeyPair .random()];
Transaction tx_1 = Transaction([ Output(Amount(1000), key_pairs[0] .address) ]);
Hash tx_1_hash = hashFull(tx_1);
scope payload_checker = new FeeManager();
scope checker = &payload_checker .check;
scope storage = new MemoryUTXOSet;
storage .put(tx_1);
// Creates the second transaction.
Transaction tx_2 = Transaction(
[Input(tx_1_hash, 0)],
// oops
[Output(Amount .invalid(-400_000), key_pairs[1] .address)]);
tx_2 .inputs[0] .unlock = signUnlock(key_pairs[0], tx_2);
assert(!tx_2 .isValid(engine, storage .getUTXOFinder(), Height(0), checker));
// Creates the third transaction.
// Reject a transaction whose output value is zero
Transaction tx_3 = Transaction(
[Input(tx_1_hash, 0)],
[Output(Amount .invalid(0), key_pairs[1] .address)]);
tx_3 .inputs[0] .unlock = signUnlock(key_pairs[0], tx_3);
assert(!tx_3 .isValid(engine, storage .getUTXOFinder(), Height(0), checker));
Example
This creates a new transaction and signs it as a publickey of the previous transaction to create and validate the input.
scope engine = new Engine();
scope storage = new MemoryUTXOSet;
immutable(KeyPair)[] key_pairs;
key_pairs ~= KeyPair .random();
key_pairs ~= KeyPair .random();
key_pairs ~= KeyPair .random();
scope payload_checker = new FeeManager();
scope checker = &payload_checker .check;
// Create the first transaction.
Transaction genesisTx = Transaction([ Output(Amount(100_000), key_pairs[0] .address) ]);
Hash genesisHash = hashFull(genesisTx);
storage .put(genesisTx);
// Create the second transaction.
Transaction tx1 = Transaction(
[
Input(genesisHash, 0)
],
[
Output(Amount(1_000), key_pairs[1] .address)
]
);
// Signs the previous hash value.
Hash tx1Hash = hashFull(tx1);
tx1 .inputs[0] .unlock = signUnlock(key_pairs[0], tx1);
storage .put(tx1);
assert(tx1 .isValid(engine, storage .getUTXOFinder(), Height(0), checker),
format("Transaction signature is not validated %s", tx1));
import agora .serialization .Serializer;
auto dupe = tx1 .serializeFull .deserializeFull!Transaction;
assert(dupe .isValid(engine, storage .getUTXOFinder(), Height(0), checker),
format("Transaction signature is not validated %s", tx1));
dupe .outputs[0] .lock .bytes .length = 0;
assert(dupe .isInvalidReason(engine, storage .getUTXOFinder(), Height(0), checker, toDelegate(utGetPenaltyDeposit))
== "LockType.Key requires 32-byte key argument in the lock script");
Transaction tx2 = Transaction(
[
Input(tx1Hash, 0)
],
[
Output(Amount(1_000), key_pairs[1] .address)
]
);
Hash tx2Hash = hashFull(tx2);
// Sign with incorrect key
tx2 .inputs[0] .unlock = signUnlock(key_pairs[2], tx2);
storage .put(tx2);
assert(!tx2 .isValid(engine, storage .getUTXOFinder(), Height(0), checker));
Example
verify transactions associated with freezing
scope engine = new Engine();
scope storage = new MemoryUTXOSet();
KeyPair[] key_pairs = [KeyPair .random, KeyPair .random, KeyPair .random, KeyPair .random];
scope payload_checker = new FeeManager();
scope checker = &payload_checker .check;
Transaction secondTx;
Hash previousHash;
// When the privious transaction type is `Payment`, second transaction type is `Freeze`.
// Second transaction is valid.
{
storage .clear;
// Create the previous transaction with type `OutputType.Payment`
Transaction previousTx = Transaction([ Output(Amount .MinFreezeAmount +
10_000 .coins, key_pairs[0] .address) ]);
previousHash = hashFull(previousTx);
foreach (idx, output; previousTx .outputs)
{
const Hash utxo_hash = hashMulti(previousHash, idx);
const UTXO utxo_value = {
unlock_height: 0,
output: output
};
storage[utxo_hash] = utxo_value;
}
// Creates the freezing transaction.
secondTx = Transaction([Input(previousHash, 0)],
[Output(Amount .MinFreezeAmount, key_pairs[1] .address, OutputType .Freeze)]
);
secondTx .inputs[0] .unlock = signUnlock(key_pairs[0], secondTx);
// Second Transaction is valid.
assert(secondTx .isValid(engine, storage .getUTXOFinder(), Height(0), checker));
}
// When the privious transaction type is `Freeze`, second transaction type is `Freeze`.
// Second transaction is invalid.
{
storage .clear;
// Create the previous transaction with type `OutputType.Payment`
Transaction previousTx = Transaction([ Output(Amount .MinFreezeAmount, key_pairs[0] .address, OutputType .Freeze) ]);
previousHash = hashFull(previousTx);
foreach (idx, output; previousTx .outputs)
{
const Hash utxo_hash = hashMulti(previousHash, idx);
const UTXO utxo_value = {
unlock_height: 0,
output: output
};
storage[utxo_hash] = utxo_value;
}
// Creates the freezing transaction.
secondTx = Transaction(
[Input(previousHash, 0)],
[Output(Amount .MinFreezeAmount, key_pairs[1] .address, OutputType .Freeze)]
);
secondTx .inputs[0] .unlock = signUnlock(key_pairs[0], secondTx);
// Second Transaction is invalid.
assert(!secondTx .isValid(engine, storage .getUTXOFinder(), Height(0), checker));
}
// When the privious transaction with not enough amount at freezing.
// Second transaction is invalid.
{
storage .clear;
// Create the previous transaction with type `OutputType.Payment`
Transaction previousTx = Transaction([ Output(Amount(100_000_000_000L), key_pairs[0] .address) ]);
previousHash = hashFull(previousTx);
foreach (idx, output; previousTx .outputs)
{
const Hash utxo_hash = hashMulti(previousHash, idx);
const UTXO utxo_value = {
unlock_height: 0,
output: output
};
storage[utxo_hash] = utxo_value;
}
// Creates the freezing transaction.
secondTx = Transaction(
[Input(previousHash, 0)],
[Output(Amount(100_000_000_000L), key_pairs[1] .address, OutputType .Freeze)]
);
secondTx .inputs[0] .unlock = signUnlock(key_pairs[0], secondTx);
// Second Transaction is invalid.
assert(!secondTx .isValid(engine, storage .getUTXOFinder(), Height(0), checker));
}
// When the privious transaction with too many amount at freezings.
// Second transaction is valid.
{
// Create the previous transaction with type `OutputType.Payment`
Transaction previousTx = Transaction([ Output(Amount(500_000_000_000L), key_pairs[0] .address) ]);
previousHash = hashFull(previousTx);
foreach (idx, output; previousTx .outputs)
{
const Hash utxo_hash = hashMulti(previousHash, idx);
const UTXO utxo_value = {
unlock_height: 0,
output: output
};
storage[utxo_hash] = utxo_value;
}
// Creates the freezing transaction.
secondTx = Transaction(
[Input(previousHash, 0)],
[Output(Amount(500_000_000_000L) - 10_000 .coins, key_pairs[1] .address, OutputType .Freeze)]
);
secondTx .inputs[0] .unlock = signUnlock(key_pairs[0], secondTx);
// Second Transaction is valid.
assert(secondTx .isValid(engine, storage .getUTXOFinder(), Height(0), checker));
}
// When the freeze TX does not include enough freezing fees
{
storage .clear;
// Create the previous transaction with type `OutputType.Payment`
Transaction previousTx = Transaction([ Output((Amount .MinFreezeAmount + 9_999 .coins) * 2, key_pairs[0] .address) ]);
previousHash = hashFull(previousTx);
foreach (idx, output; previousTx .outputs)
{
const Hash utxo_hash = hashMulti(previousHash, idx);
const UTXO utxo_value = {
unlock_height: 0,
output: output
};
storage[utxo_hash] = utxo_value;
}
// Creates the freezing transaction.
secondTx = Transaction(
[Input(previousHash, 0)],
[Output(Amount .MinFreezeAmount, key_pairs[1] .address, OutputType .Freeze),
Output(Amount .MinFreezeAmount, key_pairs[1] .address, OutputType .Freeze)]
);
secondTx .inputs[0] .unlock = signUnlock(key_pairs[0], secondTx);
// Second Transaction is invalid.
assert(!secondTx .isValid(engine, storage .getUTXOFinder(), Height(0), checker));
}
Example
Test validation of transactions associated with freezing
Table of freezing status changes over time
freezing status / melted / frozen / melting / melted
block height / N1 / N2 / N3 / N4
condition to use / / N2 >= N1+1 / N3 >= N2+1 / N4 >= N3+2016
utxo unlock height / N1+1 / N2+1 / N3+2016 / N4+1
utxo type / Payment / Freeze / Payment / Payment
scope engine = new Engine();
scope storage = new MemoryUTXOSet;
KeyPair[] key_pairs = [KeyPair .random, KeyPair .random, KeyPair .random, KeyPair .random];
scope payload_checker = new FeeManager();
scope checker = &payload_checker .check;
Height height;
Transaction secondTx;
Transaction thirdTx;
Transaction fourthTx;
Transaction fifthTx;
Hash previousHash;
Hash secondHash;
Hash thirdHash;
Hash fifthHash;
// Create the previous transaction with type <code class="lang-d"><span class="typ">OutputType<wbr/></span><span class="pun">.</span><span class="typ">Payment</span></code>
// Expected height : 0
// Expected Status : melted
{
height = 0;
Transaction previousTx = Transaction(
[ Output(Amount .MinFreezeAmount + 10_000 .coins, key_pairs[0] .address) ]);
// Save to UTXOSet
previousHash = hashFull(previousTx);
foreach (idx, output; previousTx .outputs)
{
const Hash utxo_hash = hashMulti(previousHash, idx);
const UTXO utxo_value = {
unlock height
height+1,
output
output }; storage[utxo_hash] = utxo_value; } }
// Creates the second freezing transaction // Current height : 0 // Current Status : melted // Expected height : 1 // Expected Status : frozen { height = 1; secondTx = Transaction( [Input(previousHash, 0)], [Output(Amount.MinFreezeAmount, key_pairs[1].address, OutputType.Freeze)] ); secondTx.inputs[0].unlock = signUnlock(key_pairs[0], secondTx);
// Second Transaction is VALID. assert(secondTx.isValid(engine, storage.getUTXOFinder(), height, checker));
// Save to UTXOSet secondHash = hashFull(secondTx); foreach (idx, output; secondTx.outputs) { const Hash utxo_hash = hashMulti(secondHash, idx); const UTXO utxo_value = {
unlock height
height+1,
output
output }; storage[utxo_hash] = utxo_value; } }
// Creates the third payment transaction // Current height : 1 // Current Status : frozen // Expected height : 2 // Expected Status : melting { height = 2; thirdTx = Transaction([Input(secondHash, 0)], [Output(Amount.MinFreezeAmount, key_pairs[2].address)] ); thirdTx.inputs[0].unlock = signUnlock(key_pairs[1], thirdTx);
// Third Transaction is VALID. assert(thirdTx.isValid(engine, storage.getUTXOFinder(), height, checker));
// Save to UTXOSet thirdHash = hashFull(thirdTx); foreach (idx, output; thirdTx.outputs) { const Hash utxo_hash = hashMulti(thirdHash, idx); const UTXO utxo_value = {
unlock height
height+2016,
output
output }; storage[utxo_hash] = utxo_value; } }
// Creates the fourth payment transaction : didn't change to melted not yet // Current height : 2+2014 // Current Status : melting // Expected height : 2+2015 // Expected Status : melting { height = 2+2015; // this is melting, not melted fourthTx = Transaction([Input(thirdHash, 0)], [Output(Amount.MinFreezeAmount, key_pairs[3].address)] ); fourthTx.inputs[0].unlock = signUnlock(key_pairs[2], fourthTx);
// Third Transaction is INVALID. assert(!fourthTx.isValid(engine, storage.getUTXOFinder(), height, checker)); }
// Creates the fifth payment transaction // Current height : 2+2015 // Current Status : melting // Expected height : 2+2016 // Expected Status : melted { height = 2+2016; // this is melted fifthTx = Transaction( [Input(thirdHash, 0)], [Output(Amount.MinFreezeAmount, key_pairs[3].address)] ); fifthTx.inputs[0].unlock = signUnlock(key_pairs[2], fourthTx);
// Third Transaction is VALID. assert(fifthTx.isValid(engine, storage.getUTXOFinder(), height, checker));
// Save to UTXOSet fifthHash = hashFull(fifthTx); foreach (idx, output; fifthTx.outputs) { const Hash utxo_hash = hashMulti(fifthHash, idx); const UTXO utxo_value = {
unlock height
height+1,
output
output }; storage[utxo_hash] = utxo_value; } }
Example:
test for transactions having no input or no output
import std.string; import std.algorithm.searching;
scope engine = new Engine(); scope storage = new MemoryUTXOSet; KeyPair key_pair = KeyPair.random;
scope payload_checker = new FeeManager(); scope checker = &payload_checker.check;
// create a transaction having no input Transaction oneTx = Transaction( [], [Output(Amount(50), key_pair.address)] ); storage.put(oneTx);
// test for Payment transaction having no input assert(canFind(toLower(oneTx.isInvalidReason(engine, storage.getUTXOFinder(), Height(0), checker, toDelegate(utGetPenaltyDeposit))), "no input"), format("Tx having no input should not pass validation. tx: %s", oneTx));
// create a transaction Transaction firstTx = Transaction([ Output(Amount(100_1000), key_pair.address) ]); Hash firstHash = hashFull(firstTx); storage.put(firstTx);
// create a transaction having no output Transaction secondTx = Transaction( [Input(firstHash, 0)], [] ); storage.put(secondTx);
// test for Freeze transaction having no output assert(canFind(toLower(secondTx.isInvalidReason(engine, storage.getUTXOFinder(), Height(0), checker, toDelegate(utGetPenaltyDeposit))), "no output"), format("Tx having no output should not pass validation. tx: %s", secondTx));
Example:
test for transaction having combined inputs
scope engine = new Engine(); scope storage = new MemoryUTXOSet; KeyPair[] key_pairs = [KeyPair.random, KeyPair.random];
scope payload_checker = new FeeManager(); scope checker = &payload_checker.check;
// create the first transaction. Transaction firstTx = Transaction( [Input(Hash.init, 0)], [Output(Amount(100), key_pairs[0].address)] ); Hash firstHash = hashFull(firstTx); storage.put(firstTx);
// create the second transaction. Transaction secondTx = Transaction( [Input(Hash.init, 0)], [Output(Amount(100), key_pairs[0].address, OutputType.Freeze)] ); Hash secondHash = hashFull(secondTx); storage.put(secondTx);
// create the third transaction Transaction thirdTx = Transaction( [Input(firstHash, 0), Input(secondHash, 0)], [Output(Amount(100), key_pairs[1].address)] ); Hash thirdHash = hashFull(thirdTx); storage.put(thirdTx); thirdTx.inputs[0].unlock = signUnlock(key_pairs[0], thirdTx); thirdTx.inputs[1].unlock = signUnlock(key_pairs[0], thirdTx);
// test for transaction having combined inputs assert(!thirdTx.isValid(engine, storage.getUTXOFinder(), Height(0), checker), format("Tx having combined inputs should not pass validation. tx: %s", thirdTx));
Example:
test for unknown transaction output type
scope engine = new Engine(); Transaction[Hash] storage; OutputType unknown_type = cast(OutputType)100; // picking value beyond enum range KeyPair key_pair = KeyPair.random;
scope payload_checker = new FeeManager(); scope checker = &payload_checker.check;
// create a transaction having unknown transaction type Transaction firstTx = Transaction([Input(Hash.init, 0)], [Output(Amount(100), key_pair.address, unknown_type)] ); Hash firstHash = hashFull(firstTx); storage[firstHash] = firstTx;
// test for unknown transaction type assert(!firstTx.isValid(engine, null, Height(0), checker), format("Tx having unknown type should not pass validation. tx: %s", firstTx));
Example:
test for checking input overflow for Payment and Freeze type transactions
scope engine = new Engine(); scope storage = new MemoryUTXOSet(); KeyPair[] key_pairs = [KeyPair.random, KeyPair.random];
scope payload_checker = new FeeManager(); scope checker = &payload_checker.check;
// create the first transaction auto firstTx = Transaction( [Input(Hash.init, 0)], [Output(Amount.MaxUnitSupply, key_pairs[0].address)] ); storage.put(firstTx); const firstHash = UTXO.getHash(firstTx.hashFull(), 0);
// create the second transaction auto secondTx = Transaction( [Input(Hash.init, 0)], [Output(Amount(100), key_pairs[0].address)] ); storage.put(secondTx); const secondHash = UTXO.getHash(secondTx.hashFull(), 0);
// create the third transaction auto thirdTx = Transaction( [Input(firstHash, 0), Input(secondHash, 0)], [Output(Amount(100), key_pairs[1].address)] ); storage.put(thirdTx); auto thirdHash = hashFull(thirdTx); thirdTx.inputs[0].unlock = signUnlock(key_pairs[0], thirdTx); thirdTx.inputs[1].unlock = signUnlock(key_pairs[0], thirdTx);
// test for input overflow in Payment transaction assert(!thirdTx.isValid(engine, &storage.peekUTXO, Height(0), checker), format("Tx having input overflow should not pass validation. tx: %s", thirdTx));
// create the fourth transaction auto fourthTx = Transaction( [Input(firstHash, 0), Input(secondHash, 0)], [Output(Amount(100), key_pairs[1].address, OutputType.Freeze)] ); storage.put(fourthTx); auto fourthHash = hashFull(fourthTx); fourthTx.inputs[0].unlock = signUnlock(key_pairs[0], fourthTx); fourthTx.inputs[1].unlock = signUnlock(key_pairs[0], fourthTx);
// test for input overflow in Freeze transaction assert(!fourthTx.isValid(engine, &storage.peekUTXO, Height(0), checker), format("Tx having input overflow should not pass validation. tx: %s", fourthTx));
Example:
test for checking output overflow for Payment type transaction
scope engine = new Engine(); scope storage = new MemoryUTXOSet(); KeyPair[] key_pairs = [KeyPair.random, KeyPair.random];
scope payload_checker = new FeeManager(); scope checker = &payload_checker.check;
// create the first transaction auto firstTx = Transaction( [Input(Hash.init, 0)], [Output(Amount(100), key_pairs[0].address)] ); storage.put(firstTx); const firstHash = UTXO.getHash(firstTx.hashFull(), 0);
// create the second transaction auto secondTx = Transaction( [Input(Hash.init, 0)], [Output(Amount(100), key_pairs[0].address)] ); storage.put(secondTx); const secondHash = UTXO.getHash(secondTx.hashFull(), 0);
// create the third transaction auto thirdTx = Transaction( [Input(firstHash, 0), Input(secondHash, 0)], [Output(Amount.MaxUnitSupply, key_pairs[1].address), Output(Amount(100), key_pairs[1].address)] ); storage.put(thirdTx); auto thirdHash = hashFull(thirdTx); thirdTx.inputs[0].unlock = signUnlock(key_pairs[0], thirdTx); thirdTx.inputs[1].unlock = signUnlock(key_pairs[0], thirdTx);
// test for output overflow in Payment transaction assert(!thirdTx.isValid(engine, &storage.peekUTXO, Height(0), checker), format("Tx having output overflow should not pass validation. tx: %s", thirdTx));
Example:
test for transaction to store data
scope engine = new Engine(); scope storage = new MemoryUTXOSet; KeyPair key_pair = KeyPair.random;
scope payload_checker = new FeeManager(); scope checker = &payload_checker.check;
// create the payment transaction. Transaction paymentTx = Transaction( [Input(Hash.init)], [Output(80_000.coins, key_pair.address)] ); storage.put(paymentTx); Hash payment_utxo = UTXO.getHash(paymentTx.hashFull(), 0);
// create the frozen transaction. Transaction frozenTx = Transaction( [Input(Hash.init)], [Output(80_000.coins, key_pair.address, OutputType.Freeze)] ); storage.put(frozenTx); Hash frozen_utxo = UTXO.getHash(frozenTx.hashFull(), 0);
// create data with nomal size ubyte[] normal_data; normal_data.length = payload_checker.params.TxPayloadMaxSize; foreach (idx; 0 .. normal_data.length) normal_data[idx] = cast(ubyte)(idx % 256);
// create data with large size ubyte[] large_data; large_data ~= normal_data; large_data ~= cast(ubyte)(0);
// calculate fee Amount normal_data_fee = calculateDataFee(normal_data.length, payload_checker.params.TxPayloadFeeFactor); Amount large_data_fee = calculateDataFee(large_data.length, payload_checker.params.TxPayloadFeeFactor);
Transaction dataTx; Hash dataHash;
// Test 1. Too large data // create a transaction with large data dataTx = Transaction( [Input(payment_utxo)], [ Output(large_data_fee, payload_checker.params.CommonsBudgetAddress), Output(40_000.coins, key_pair.address) ].sort.array, large_data, ); dataHash = hashFull(dataTx); dataTx.inputs[0].unlock = signUnlock(key_pair, dataTx);
// test for the transaction with large data assert(!dataTx.isValid(engine, &storage.peekUTXO, Height(0), checker), format("When storing data, tx with large data payload should not pass validation. tx: %s", dataTx));
// Test 2. With not enough fee // create a transaction with not enough fee dataTx = Transaction( [Input(payment_utxo)], [Output(Amount(80_000L * 10_000_000L - normal_data_fee.integral + 1), key_pair.address)], normal_data, ); dataHash = hashFull(dataTx); dataTx.inputs[0].unlock = signUnlock(key_pair, dataTx);
// test for transaction without commons budget assert(!dataTx.isValid(engine, &storage.peekUTXO, Height(0), checker), format("When storing data, tx with not enough fee should not pass validation. tx: %s", dataTx));
// Test 3. Nomal // create a transaction with enough fee Amount rem_amount = paymentTx.outputs[0].value; rem_amount.sub(normal_data_fee); dataTx = Transaction( [Input(payment_utxo)], [Output(rem_amount, key_pair.address)], normal_data, ); dataHash = hashFull(dataTx); dataTx.inputs[0].unlock = signUnlock(key_pair, dataTx);
// test for the transaction with enough fee assert(dataTx.isValid(engine, &storage.peekUTXO, Height(0), checker), format("When storing data, Transaction data is not validated. tx: %s", dataTx));
// Test 5. Using frozen input // create the data transaction. dataTx = Transaction( [Input(frozen_utxo)], [ Output(40_000.coins, key_pair.address) ], normal_data, ); dataHash = hashFull(dataTx); dataTx.inputs[0].unlock = signUnlock(key_pair, dataTx);
// test for data storage using frozen input assert(dataTx.isValid(engine, &storage.peekUTXO, Height(0), checker), format("When storing data, tx with frozen input should pass validation. tx: %s", dataTx));
// Test 6. The transaction with the type of Freeze // create the data transaction. dataTx = Transaction( [Input(payment_utxo)], [ Output(40_000.coins, key_pair.address, OutputType.Freeze) ], normal_data, ); dataHash = hashFull(dataTx); dataTx.inputs[0].unlock = signUnlock(key_pair, dataTx);
// test for data storage using frozen input assert(!dataTx.isValid(engine, &storage.peekUTXO, Height(0), checker), format("When storing data, tx with type of Freeze should not pass validation. tx: %s", dataTx));
Example:
transaction-level absolute time lock
scope engine = new Engine(); scope storage = new MemoryUTXOSet; scope payload_checker = new FeeManager(); scope checker = &payload_checker.check;
KeyPair kp = KeyPair.random();
Transaction prev_tx = Transaction([Output(Amount(100), kp.address)]); storage.put(prev_tx);
Transaction tx = Transaction( [Input(hashFull(prev_tx), 0)], [Output(Amount(50), kp.address)]);
// effectively disabled lock tx.lock_height = Height(0); tx.inputs[0].unlock = signUnlock(kp, tx); assert(tx.isInvalidReason(engine, storage.getUTXOFinder(), Height(0), checker, toDelegate(utGetPenaltyDeposit)) is null); assert(tx.isInvalidReason(engine, storage.getUTXOFinder(), Height(1024), checker, toDelegate(utGetPenaltyDeposit)) is null);
tx.lock_height = Height(10); tx.inputs[0].unlock = signUnlock(kp, tx); assert(tx.isInvalidReason(engine, storage.getUTXOFinder(), Height(0), checker, toDelegate(utGetPenaltyDeposit)) == "Transaction: Not unlocked for this height"); assert(tx.isInvalidReason(engine, storage.getUTXOFinder(), Height(9), checker, toDelegate(utGetPenaltyDeposit)) == "Transaction: Not unlocked for this height"); assert(tx.isInvalidReason(engine, storage.getUTXOFinder(), Height(10), checker, toDelegate(utGetPenaltyDeposit)) == null); assert(tx.isInvalidReason(engine, storage.getUTXOFinder(), Height(1024), checker, toDelegate(utGetPenaltyDeposit)) == null);