1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// SPDX-License-Identifier: CC0-1.0
use core::fmt;
use primitives::transaction::OutPoint;
/// Script validation errors produced by the Tidecoin validation engine.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScriptError {
/// Default value used before script execution sets a more specific error.
Ok,
/// Generic script failure when no more specific code is available.
Unknown,
/// Script evaluation left a false top stack item.
EvalFalse,
/// `OP_RETURN` aborted script execution.
OpReturn,
/// Script exceeded the maximum allowed byte size.
ScriptSize,
/// A pushed element exceeded the maximum allowed byte size.
PushSize,
/// Script exceeded the maximum allowed opcode count.
OpCount,
/// Script exceeded the maximum allowed stack size.
StackSize,
/// Multisig signature count was invalid.
SigCount,
/// Multisig public-key count was invalid.
PubkeyCount,
/// `OP_VERIFY` failed.
Verify,
/// `OP_EQUALVERIFY` failed.
EqualVerify,
/// `OP_CHECKSIGVERIFY` failed.
CheckSigVerify,
/// `OP_CHECKMULTISIGVERIFY` failed.
CheckMultiSigVerify,
/// `OP_NUMEQUALVERIFY` failed.
NumEqualVerify,
/// Script contained an invalid opcode.
BadOpcode,
/// `OP_CODESEPARATOR` was disallowed by the active flags.
OpCodeSeparator,
/// Script used a disabled opcode.
DisabledOpcode,
/// Script attempted an invalid main-stack operation.
InvalidStackOperation,
/// Script attempted an invalid alt-stack operation.
InvalidAltstackOperation,
/// Script conditionals were unbalanced.
UnbalancedConditional,
/// Locktime argument was negative.
NegativeLockTime,
/// Locktime requirement was not satisfied.
UnsatisfiedLockTime,
/// Signature hash type was invalid for the current validation path.
SigHashType,
/// Non-minimal pushdata encoding was used when forbidden.
MinimalData,
/// `scriptSig` was not push-only when required.
SigPushOnly,
/// Dummy stack argument for multisig was not null.
SigNullDummy,
/// Cleanstack rule was violated.
CleanStack,
/// `MINIMALIF` rule was violated.
MinimalIf,
/// `NULLFAIL` rule was violated.
NullFail,
/// Upgradable NOPs were discouraged and encountered.
DiscourageUpgradableNops,
/// Upgradable witness programs were discouraged and encountered.
DiscourageUpgradableWitnessProgram,
/// Witness program length did not match the expected form.
WitnessProgramWrongLength,
/// Witness program required witness items but found none.
WitnessProgramWitnessEmpty,
/// Witness program did not match the executed witness script.
WitnessProgramMismatch,
/// Native witness spend was malleated.
WitnessMalleated,
/// P2SH-wrapped witness spend was malleated.
WitnessMalleatedP2SH,
/// Unexpected witness data was present.
WitnessUnexpected,
/// Signature find-and-delete was disallowed by the active flags.
SigFindAndDelete,
}
impl fmt::Display for ScriptError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Ok => "OK",
Self::Unknown => "UNKNOWN",
Self::EvalFalse => "EVAL_FALSE",
Self::OpReturn => "OP_RETURN",
Self::ScriptSize => "SCRIPT_SIZE",
Self::PushSize => "PUSH_SIZE",
Self::OpCount => "OP_COUNT",
Self::StackSize => "STACK_SIZE",
Self::SigCount => "SIG_COUNT",
Self::PubkeyCount => "PUBKEY_COUNT",
Self::Verify => "VERIFY",
Self::EqualVerify => "EQUALVERIFY",
Self::CheckSigVerify => "CHECKSIGVERIFY",
Self::CheckMultiSigVerify => "CHECKMULTISIGVERIFY",
Self::NumEqualVerify => "NUMEQUALVERIFY",
Self::BadOpcode => "BAD_OPCODE",
Self::OpCodeSeparator => "OP_CODESEPARATOR",
Self::DisabledOpcode => "DISABLED_OPCODE",
Self::InvalidStackOperation => "INVALID_STACK_OPERATION",
Self::InvalidAltstackOperation => "INVALID_ALTSTACK_OPERATION",
Self::UnbalancedConditional => "UNBALANCED_CONDITIONAL",
Self::NegativeLockTime => "NEGATIVE_LOCKTIME",
Self::UnsatisfiedLockTime => "UNSATISFIED_LOCKTIME",
Self::SigHashType => "SIG_HASHTYPE",
Self::MinimalData => "MINIMALDATA",
Self::SigPushOnly => "SIG_PUSHONLY",
Self::SigNullDummy => "SIG_NULLDUMMY",
Self::CleanStack => "CLEANSTACK",
Self::MinimalIf => "MINIMALIF",
Self::NullFail => "NULLFAIL",
Self::DiscourageUpgradableNops => "DISCOURAGE_UPGRADABLE_NOPS",
Self::DiscourageUpgradableWitnessProgram => "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM",
Self::WitnessProgramWrongLength => "WITNESS_PROGRAM_WRONG_LENGTH",
Self::WitnessProgramWitnessEmpty => "WITNESS_PROGRAM_WITNESS_EMPTY",
Self::WitnessProgramMismatch => "WITNESS_PROGRAM_MISMATCH",
Self::WitnessMalleated => "WITNESS_MALLEATED",
Self::WitnessMalleatedP2SH => "WITNESS_MALLEATED_P2SH",
Self::WitnessUnexpected => "WITNESS_UNEXPECTED",
Self::SigFindAndDelete => "SIG_FINDANDDELETE",
};
f.write_str(s)
}
}
/// High-level validation errors produced by the Tidecoin validation engine.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TidecoinValidationError {
/// Script execution reached the consensus engine and failed with a specific
/// script error.
///
/// Callers should treat this as a consensus-invalid spend for the selected
/// script, witness, amount, and flag combination rather than as malformed
/// caller input.
Script(ScriptError),
/// The requested input index does not exist in the spending transaction.
///
/// This indicates a caller-side setup error. Retrying with the same input
/// index and transaction will fail again.
InvalidInputIndex {
/// The requested input index.
index: usize,
/// The total number of inputs in the spending transaction.
inputs: usize,
},
/// Script verification flags were invalid.
///
/// This indicates the caller supplied unsupported or inconsistent
/// verification bits rather than a consensus-invalid script.
InvalidFlags,
/// Input amount is required for the selected witness verification path.
///
/// This indicates the caller attempted a witness-aware verification path
/// without providing the spent output amount required by the engine.
AmountRequired,
/// A spent output needed for validation was missing.
///
/// This indicates required prevout state was unavailable at the engine
/// boundary. It is a missing-context error, not a script failure.
MissingSpentOutput(OutPoint),
}
impl fmt::Display for TidecoinValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Script(err) => write!(f, "script validation failed: {}", err),
Self::InvalidInputIndex { index, inputs } => {
write!(
f,
"input index {} out of bounds for transaction with {} inputs",
index, inputs
)
}
Self::InvalidFlags => f.write_str("script verification flags are invalid"),
Self::AmountRequired => {
f.write_str("input amount is required if witness verification is used")
}
Self::MissingSpentOutput(outpoint) => {
write!(f, "missing spent output {:?}", outpoint)
}
}
}
}
impl From<ScriptError> for TidecoinValidationError {
fn from(value: ScriptError) -> Self {
Self::Script(value)
}
}
#[cfg(feature = "std")]
impl std::error::Error for TidecoinValidationError {}