Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/ed25519-verify.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added ed25519-verify to clarity6
5 changes: 2 additions & 3 deletions clarity/src/vm/analysis/arithmetic_checker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,8 @@ impl ArithmeticOnlyChecker<'_> {
Err(Error::FunctionNotPermitted(function))
}
Sha512 | Sha512Trunc256 | Secp256k1Recover | Secp256k1Verify | Secp256r1Verify
| Hash160 | Sha256 | Keccak256 | VerifyMerkleProof | GetBitcoinTxOutput => {
Err(Error::FunctionNotPermitted(function))
}
| Ed25519Verify | Hash160 | Sha256 | Keccak256 | VerifyMerkleProof
| GetBitcoinTxOutput => Err(Error::FunctionNotPermitted(function)),
Add | Subtract | Divide | Multiply | CmpGeq | CmpLeq | CmpLess | CmpGreater
| Modulo | Power | Sqrti | Log2 | BitwiseXor | And | Or | Not | Equals | If
| ConsSome | ConsOkay | ConsError | DefaultTo | UnwrapRet | UnwrapErrRet | IsOkay
Expand Down
1 change: 1 addition & 0 deletions clarity/src/vm/analysis/read_only_checker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ impl<'a, 'b> ReadOnlyChecker<'a, 'b> {
| Secp256k1Recover
| Secp256k1Verify
| Secp256r1Verify
| Ed25519Verify
| ConsSome
| ConsOkay
| ConsError
Expand Down
3 changes: 2 additions & 1 deletion clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,8 @@ impl TypedNativeFunction {
| AllowanceAll
| Secp256r1Verify
| VerifyMerkleProof
| GetBitcoinTxOutput => {
| GetBitcoinTxOutput
| Ed25519Verify => {
return Err(StaticCheckErrorKind::Unreachable(
"Clarity 2+ keywords should not show up in 2.05".into(),
));
Expand Down
13 changes: 13 additions & 0 deletions clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,18 @@ fn check_secp256r1_verify(
Ok(TypeSignature::BoolType)
}

fn check_ed25519_verify(
checker: &mut TypeChecker,
args: &[SymbolicExpression],
context: &TypingContext,
) -> Result<TypeSignature, StaticCheckError> {
check_argument_count(3, args)?;
Comment thread
rob-stacks marked this conversation as resolved.
checker.type_check_expects(&args[0], context, &TypeSignature::BUFFER_MAX)?;
checker.type_check_expects(&args[1], context, &TypeSignature::BUFFER_64)?;
checker.type_check_expects(&args[2], context, &TypeSignature::BUFFER_32)?;
Ok(TypeSignature::BoolType)
}

fn check_get_block_info(
checker: &mut TypeChecker,
args: &[SymbolicExpression],
Expand Down Expand Up @@ -1334,6 +1346,7 @@ impl TypedNativeFunction {
Secp256r1Verify => Special(SpecialNativeFunction(&check_secp256r1_verify)),
VerifyMerkleProof => Special(SpecialNativeFunction(&check_verify_merkle_proof)),
GetBitcoinTxOutput => Special(SpecialNativeFunction(&check_get_bitcoin_tx_output)),
Ed25519Verify => Special(SpecialNativeFunction(&check_ed25519_verify)),
};

Ok(out)
Expand Down
3 changes: 3 additions & 0 deletions clarity/src/vm/costs/cost_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ define_named_enum!(ClarityCostFunction {
Secp256r1verify("cost_secp256r1verify"),
VerifyMerkleProof("cost_verify_merkle_proof"),
GetBitcoinTxOutput("cost_get_bitcoin_tx_output"),
Ed25519verify("cost_ed25519verify"),
Unimplemented("cost_unimplemented"),
});

Expand Down Expand Up @@ -343,6 +344,7 @@ pub trait CostValues {
fn cost_secp256r1verify(n: u64) -> Result<ExecutionCost, VmExecutionError>;
fn cost_verify_merkle_proof(n: u64) -> Result<ExecutionCost, VmExecutionError>;
fn cost_get_bitcoin_tx_output(n: u64) -> Result<ExecutionCost, VmExecutionError>;
fn cost_ed25519verify(n: u64) -> Result<ExecutionCost, VmExecutionError>;
}

impl ClarityCostFunction {
Expand Down Expand Up @@ -502,6 +504,7 @@ impl ClarityCostFunction {
ClarityCostFunction::Secp256r1verify => C::cost_secp256r1verify(n),
ClarityCostFunction::VerifyMerkleProof => C::cost_verify_merkle_proof(n),
ClarityCostFunction::GetBitcoinTxOutput => C::cost_get_bitcoin_tx_output(n),
ClarityCostFunction::Ed25519verify => C::cost_ed25519verify(n),
ClarityCostFunction::Unimplemented => Err(RuntimeError::NotImplemented.into()),
}
}
Expand Down
4 changes: 4 additions & 0 deletions clarity/src/vm/costs/costs_1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,4 +773,8 @@ impl CostValues for Costs1 {
fn cost_get_bitcoin_tx_output(_n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}

fn cost_ed25519verify(n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}
}
4 changes: 4 additions & 0 deletions clarity/src/vm/costs/costs_2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,4 +773,8 @@ impl CostValues for Costs2 {
fn cost_get_bitcoin_tx_output(_n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}

fn cost_ed25519verify(n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}
}
4 changes: 4 additions & 0 deletions clarity/src/vm/costs/costs_2_testnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -773,4 +773,8 @@ impl CostValues for Costs2Testnet {
fn cost_get_bitcoin_tx_output(_n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}

fn cost_ed25519verify(n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}
}
4 changes: 4 additions & 0 deletions clarity/src/vm/costs/costs_3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -791,4 +791,8 @@ impl CostValues for Costs3 {
fn cost_get_bitcoin_tx_output(_n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}

fn cost_ed25519verify(n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}
}
4 changes: 4 additions & 0 deletions clarity/src/vm/costs/costs_4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,4 +481,8 @@ impl CostValues for Costs4 {
fn cost_get_bitcoin_tx_output(_n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}

fn cost_ed25519verify(n: u64) -> Result<ExecutionCost, VmExecutionError> {
Err(RuntimeError::NotImplemented.into())
}
}
4 changes: 4 additions & 0 deletions clarity/src/vm/costs/costs_5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,4 +474,8 @@ impl CostValues for Costs5 {
fn cost_get_bitcoin_tx_output(n: u64) -> Result<ExecutionCost, VmExecutionError> {
Ok(ExecutionCost::runtime(linear(n >> 10, 125, 291)))
}

fn cost_ed25519verify(n: u64) -> Result<ExecutionCost, VmExecutionError> {
Ok(ExecutionCost::runtime(linear(n >> 10, 125, 7880)))
}
}
18 changes: 18 additions & 0 deletions clarity/src/vm/docs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1486,6 +1486,23 @@ without trusting the caller to have correctly hashed or stripped witness data fr
(get-bitcoin-tx-output? 0x00 u0) ;; Returns (err u1)",
};

const ED25519VERIFY_API: SpecialAPI = SpecialAPI {
input_type: "(buff 1048576), (buff 64), (buff 32)",
snippet: "ed25519-verify ${1:message} ${2:signature} ${3:public-key})",
output_type: "bool",
signature: "(ed25519-verify message signature public-key)",
description: "The `ed25519-verify` function verifies that the provided signature of the message
was signed with the private key that generated the public key.
The `message` can be up to 1 MiB in size. The `signature` is the raw 64-byte signature, and the `public-key` is the raw 32-byte public key.
returns `true` if the signature is valid, and `false` otherwise.
Note that validation is in strict mode, so non-canonical signatures will be rejected.",
example: "(ed25519-verify 0xaf82
0x6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a
0xfc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025) ;; Returns true
(ed25519-verify 0x00000000000000000000000000000000000000 0x6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a
0xfc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025) ;; Returns false"
};

const CONTRACT_CALL_API: SpecialAPI = SpecialAPI {
input_type: "ContractName, PublicFunctionName, Arg0, ...",
snippet: "contract-call? ${1:contract-principal} ${2:func} ${3:arg1}",
Expand Down Expand Up @@ -2970,6 +2987,7 @@ pub fn make_api_reference(function: &NativeFunctions) -> FunctionAPI {
Secp256r1Verify => make_for_special(&SECP256R1VERIFY_API, function),
VerifyMerkleProof => make_for_special(&VERIFY_MERKLE_PROOF_API, function),
GetBitcoinTxOutput => make_for_special(&GET_BITCOIN_TX_OUTPUT_API, function),
Ed25519Verify => make_for_special(&ED25519VERIFY_API, function),
}
}

Expand Down
73 changes: 73 additions & 0 deletions clarity/src/vm/functions/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

use clarity_types::types::MAX_VALUE_SIZE;
use stacks_common::address::{
AddressHashMode, C32_ADDRESS_VERSION_MAINNET_SINGLESIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
};
use stacks_common::types::chainstate::StacksAddress;
use stacks_common::util::ed25519::ed25519_verify;
use stacks_common::util::hash;
use stacks_common::util::secp256k1::{Secp256k1PublicKey, secp256k1_recover, secp256k1_verify};
use stacks_common::util::secp256r1::{secp256r1_verify, secp256r1_verify_digest};
Expand Down Expand Up @@ -331,3 +333,74 @@ pub fn special_secp256r1_verify(

Ok(Value::Bool(verify_result.is_ok()))
}

pub fn special_ed25519_verify(
args: &[SymbolicExpression],
exec_state: &mut ExecutionState,
invoke_ctx: &InvocationContext,
context: &LocalContext,
) -> Result<Value, VmExecutionError> {
// (ed25519-verify message signature public-key)
// message: (buff MAX_VALUE_SIZE), signature: (buff 64), public-key: (buff 32)
check_argument_count(3, args)?;
Comment thread
rob-stacks marked this conversation as resolved.
Outdated

runtime_cost(ClarityCostFunction::Ed25519verify, exec_state, 0)?;
Comment thread
rob-stacks marked this conversation as resolved.
Outdated

let arg0 = args
.first()
.ok_or(RuntimeCheckErrorKind::IncorrectArgumentCount(0, 3))?;
let message_value = eval(arg0, exec_state, invoke_ctx, context)?;
let message = match message_value.as_ref() {
Value::Sequence(SequenceData::Buffer(BuffData { data }))
if data.len() <= MAX_VALUE_SIZE as usize =>
{
data
}
_ => {
return Err(RuntimeCheckErrorKind::TypeValueError(
Box::new(TypeSignature::BUFFER_MAX),
message_value.as_ref().to_error_string(),
)
.into());
}
};

let arg1 = args
.get(1)
.ok_or(RuntimeCheckErrorKind::IncorrectArgumentCount(1, 3))?;
let signature_value = eval(arg1, exec_state, invoke_ctx, context)?;
let signature = match signature_value.as_ref() {
Value::Sequence(SequenceData::Buffer(BuffData { data })) if data.len() <= 64 => {
if data.len() != 64 {
return Ok(Value::Bool(false));
}
data
}
_ => {
return Err(RuntimeCheckErrorKind::TypeValueError(
Box::new(TypeSignature::BUFFER_64),
signature_value.as_ref().to_error_string(),
)
.into());
}
};

let arg2 = args
.get(2)
.ok_or(RuntimeCheckErrorKind::IncorrectArgumentCount(2, 3))?;
let pubkey_value = eval(arg2, exec_state, invoke_ctx, context)?;
let pubkey = match pubkey_value.as_ref() {
Value::Sequence(SequenceData::Buffer(BuffData { data })) if data.len() == 32 => data,
_ => {
return Err(RuntimeCheckErrorKind::TypeValueError(
Box::new(TypeSignature::BUFFER_32),
pubkey_value.as_ref().to_error_string(),
)
.into());
}
};

let verify_result = ed25519_verify(message, signature, pubkey);

Ok(Value::Bool(verify_result.is_ok()))
}
4 changes: 4 additions & 0 deletions clarity/src/vm/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ define_versioned_named_enum_with_max!(NativeFunctions(ClarityVersion) {
Secp256r1Verify("secp256r1-verify", ClarityVersion::Clarity4, None),
VerifyMerkleProof("verify-merkle-proof", ClarityVersion::Clarity6, None),
GetBitcoinTxOutput("get-bitcoin-tx-output?", ClarityVersion::Clarity6, None),
Ed25519Verify("ed25519-verify", ClarityVersion::Clarity6, None),
});

///
Expand Down Expand Up @@ -591,6 +592,9 @@ pub fn lookup_reserved_functions(name: &str, version: &ClarityVersion) -> Option
ClarityCostFunction::GetBitcoinTxOutput,
&bitcoin::cost_input_get_bitcoin_tx_output,
),
Ed25519Verify => {
SpecialFunction("native_ed25519-verify", &crypto::special_ed25519_verify)
Comment thread
rob-stacks marked this conversation as resolved.
Outdated
}
};
Some(callable)
} else {
Expand Down
Loading
Loading