Function getChallenge
Gets the challenge hash for the provided transaction, input index,
and the type of SigHash. This cannot be folded into a sign
routine
because it's also required during signature validation.
geod24 .bitblob .BitBlob!(64L) getChallenge
(
in ref const(Transaction) tx,
in const(SigHash) sig_hash = SigHash .All,
in const(ulong) input_idx = 0LU,
in const(ulong) output_idx = 0LU
) nothrow @safe;
The input index is only used for some types of SigHash (SigHash.NoInput).
Parameters
Name | Description |
---|---|
tx | the transaction to sign |
sig_hash | the SigHash to use |
input_idx | the associated input index we're signing for |
output_idx | the associated output index we're signing for |
Returns
the challenge as a hash
Example
import agora .crypto .Key;
import agora .common .Amount;
import agora .utils .Test;
// SigHash.All
{
auto tx = Transaction([Input(hashFull(1)), Input(hashFull(2))], [Output(Amount(1), PublicKey .init)], Height(10));
auto challenge_idx_0 = getChallenge(tx, SigHash .All, 0);
assert(challenge_idx_0 != tx .hashFull());
assert(challenge_idx_0 == getChallenge(tx, SigHash .All, 1));
tx .inputs[0] = Input(hashFull(3));
assert(challenge_idx_0 != getChallenge(tx, SigHash .All, 1));
}
// SigHash.NoInput
{
auto tx = Transaction([Input(hashFull(1)), Input(hashFull(2))], [Output(Amount(1), PublicKey .init)], Height(10));
auto challenge_idx_0 = getChallenge(tx, SigHash .NoInput, 0);
assert(challenge_idx_0 != getChallenge(tx, SigHash .NoInput, 1));
// Redirect input_0
tx .inputs[0] = Input(hashFull(3));
assert(challenge_idx_0 == getChallenge(tx, SigHash .NoInput, 0));
// Redirect input_1
tx .inputs[1] = Input(hashFull(4));
assert(challenge_idx_0 != getChallenge(tx, SigHash .NoInput, 0));
// restore input_1
tx .inputs[1] = Input(hashFull(2));
// add a new output
tx .outputs ~= Output(Amount(2), PublicKey .init);
assert(challenge_idx_0 != getChallenge(tx, SigHash .NoInput, 0));
}
// SigHash.Single
{
auto tx = Transaction([Input(hashFull(1)), Input(hashFull(2))], [Output(Amount(1), PublicKey .init)], Height(10));
auto challenge_idx_0 = getChallenge(tx, SigHash .Single, 0, 0);
// since SigHash.Single signs all inputs, challenges for different inputs should be the same
assert(challenge_idx_0 == getChallenge(tx, SigHash .Single, 1, 0));
// add a new not signed output
tx .outputs ~= Output(Amount(2), PublicKey .init);
// old challenge should still hold
assert(challenge_idx_0 == getChallenge(tx, SigHash .Single, 0, 0));
assert(challenge_idx_0 != getChallenge(tx, SigHash .Single, 0, 1));
tx .outputs = Output(Amount(0), PublicKey .init) ~ tx .outputs;
// output changes index, after updating the index challenge should hold
assert(challenge_idx_0 == getChallenge(tx, SigHash .Single, 0, 1));
// Redirect input_0
tx .inputs[0] = Input(hashFull(3));
assert(challenge_idx_0 != getChallenge(tx, SigHash .Single, 0, 1));
// restore input_0 and add a new input
tx .inputs[0] = Input(hashFull(1));
tx .inputs ~= Input(hashFull(3));
tx .inputs .sort();
assert(challenge_idx_0 != getChallenge(tx, SigHash .Single, 0, 1));
}
// SigHash.Single | SigHash.AnyoneCanPay
{
auto tx = Transaction([Input(hashFull(1)), Input(hashFull(2))], [Output(Amount(1), PublicKey .init)], Height(10));
auto challenge_idx_0 = getChallenge(tx, SigHash .Single_AnyoneCanPay, 0, 0);
// add a new input, challenge should hold
tx .inputs ~= Input(hashFull(0));
assert(challenge_idx_0 == getChallenge(tx, SigHash .Single_AnyoneCanPay, 0, 0));
// change an existing input, challenge should hold
tx .inputs[1] = Input(hashFull(3));
assert(challenge_idx_0 == getChallenge(tx, SigHash .Single_AnyoneCanPay, 0, 0));
tx .inputs = Input .init ~ tx .inputs;
// change input index, challenge should hold
assert(challenge_idx_0 == getChallenge(tx, SigHash .Single_AnyoneCanPay, 1, 0));
}
// SigHash.Single | SigHash.NoInput | SigHash.AnyoneCanPay
{
auto tx = Transaction([Input(hashFull(1)), Input(hashFull(2))], [Output(Amount(1), PublicKey .init)], Height(10));
auto challenge_idx_0 = getChallenge(tx, SigHash .Single_NoInput_AnyoneCanPay, 0, 0);
// change signed input, challenge should hold
tx .inputs[0] = Input .init;
assert(challenge_idx_0 == getChallenge(tx, SigHash .Single_NoInput_AnyoneCanPay, 0, 0));
}
// SigHash.OmitSingle
{
auto tx = Transaction([Input(hashFull(1)), Input(hashFull(2))],
[Output(Amount(1), PublicKey .init), Output(Amount(2), PublicKey .init)], Height(10));
auto challenge_idx_0 = getChallenge(tx, SigHash .OmitSingle, 0, 0);
// cannot add a new input
tx .inputs ~= Input(hashFull(0));
assert(challenge_idx_0 != getChallenge(tx, SigHash .OmitSingle, 0, 0));
// revert
tx .inputs = tx .inputs[0 .. $ - 1];
auto old_input_0 = tx .inputs[0];
// cannot change an input
tx .inputs[0] = Input(hashFull(0));
assert(challenge_idx_0 != getChallenge(tx, SigHash .OmitSingle, 0, 0));
// revert
tx .inputs[0] = old_input_0;
auto old_output_1_value = tx .outputs[1] .value;
// cannot change signed output
tx .outputs[1] .value = Amount(3);
assert(challenge_idx_0 != getChallenge(tx, SigHash .OmitSingle, 0, 0));
// revert
tx .outputs[1] .value = old_output_1_value;
// can change omitted output
tx .outputs[0] .value = Amount(3);
assert(challenge_idx_0 == getChallenge(tx, SigHash .OmitSingle, 0, 0));
// cannot add a new output
tx .outputs ~= Output .init;
assert(challenge_idx_0 != getChallenge(tx, SigHash .OmitSingle, 0, 0));
}
// SigHash.OmitSingle | SigHash.NoInput | SigHash.AnyoneCanPay
{
auto tx = Transaction([Input(hashFull(1)), Input(hashFull(2))], [Output(Amount(1), PublicKey .init)], Height(10));
auto challenge_idx_0 = getChallenge(tx, SigHash .OmitSingle_NoInput_AnyoneCanPay, 0, 0);
// change signed input, challenge should hold
tx .inputs[0] = Input .init;
assert(challenge_idx_0 == getChallenge(tx, SigHash .OmitSingle_NoInput_AnyoneCanPay, 0, 0));
}