@@ -14,14 +14,14 @@ use clarity::types::chainstate::{
14
14
} ;
15
15
use clarity:: types:: chainstate:: { StacksAddress , StacksPublicKey } ;
16
16
use clarity:: types:: { PrivateKey , StacksEpochId } ;
17
- use clarity:: util:: hash:: { Hash160 , Sha512Trunc256Sum } ;
17
+ use clarity:: util:: hash:: { Hash160 , Sha256Sum , Sha512Trunc256Sum } ;
18
18
use clarity:: util:: retry:: BoundReader ;
19
19
use clarity:: util:: secp256k1:: {
20
20
MessageSignature , Secp256k1PrivateKey , Secp256k1PublicKey , MESSAGE_SIGNATURE_ENCODED_SIZE ,
21
21
} ;
22
22
use clarity:: util:: vrf:: VRFProof ;
23
23
use clarity:: vm:: types:: {
24
- PrincipalData , QualifiedContractIdentifier , StandardPrincipalData , Value ,
24
+ PrincipalData , QualifiedContractIdentifier , StandardPrincipalData , TupleData , Value ,
25
25
} ;
26
26
use clarity:: vm:: ClarityVersion ;
27
27
use clarity:: vm:: { ClarityName , ContractName } ;
@@ -4222,7 +4222,7 @@ impl SignerMessageMetadata {
4222
4222
#[ derive( Debug , Clone , PartialEq , Serialize , Deserialize ) ]
4223
4223
pub struct MockSignature {
4224
4224
/// The signer's signature across the mock proposal
4225
- signature : MessageSignature ,
4225
+ pub signature : MessageSignature ,
4226
4226
/// The mock block proposal that was signed across
4227
4227
pub mock_proposal : MockProposal ,
4228
4228
/// The signature metadata
@@ -4316,7 +4316,103 @@ pub struct MockProposal {
4316
4316
/// The view of the stacks node peer information at the time of the mock proposal
4317
4317
pub peer_info : PeerInfo ,
4318
4318
/// The miner's signature across the peer info
4319
- signature : MessageSignature ,
4319
+ pub signature : MessageSignature ,
4320
+ }
4321
+
4322
+ // Helper function to generate domain for structured data hash
4323
+ pub fn make_structured_data_domain ( name : & str , version : & str , chain_id : u32 ) -> Value {
4324
+ Value :: Tuple (
4325
+ TupleData :: from_data ( vec ! [
4326
+ (
4327
+ "name" . into( ) ,
4328
+ Value :: string_ascii_from_bytes( name. into( ) ) . unwrap( ) ,
4329
+ ) ,
4330
+ (
4331
+ "version" . into( ) ,
4332
+ Value :: string_ascii_from_bytes( version. into( ) ) . unwrap( ) ,
4333
+ ) ,
4334
+ ( "chain-id" . into( ) , Value :: UInt ( chain_id. into( ) ) ) ,
4335
+ ] )
4336
+ . unwrap ( ) ,
4337
+ )
4338
+ }
4339
+
4340
+ /// Message prefix for signed structured data. "SIP018" in ascii
4341
+ pub const STRUCTURED_DATA_PREFIX : [ u8 ; 6 ] = [ 0x53 , 0x49 , 0x50 , 0x30 , 0x31 , 0x38 ] ;
4342
+
4343
+ pub fn structured_data_hash ( value : Value ) -> Sha256Sum {
4344
+ let mut bytes = vec ! [ ] ;
4345
+ value. serialize_write ( & mut bytes) . unwrap ( ) ;
4346
+ Sha256Sum :: from_data ( bytes. as_slice ( ) )
4347
+ }
4348
+
4349
+ /// Generate a message hash for signing structured Clarity data.
4350
+ /// Reference [SIP018](https://github.com/stacksgov/sips/blob/main/sips/sip-018/sip-018-signed-structured-data.md) for more information.
4351
+ pub fn structured_data_message_hash ( structured_data : Value , domain : Value ) -> Sha256Sum {
4352
+ let message = [
4353
+ STRUCTURED_DATA_PREFIX . as_ref ( ) ,
4354
+ structured_data_hash ( domain) . as_bytes ( ) ,
4355
+ structured_data_hash ( structured_data) . as_bytes ( ) ,
4356
+ ]
4357
+ . concat ( ) ;
4358
+
4359
+ Sha256Sum :: from_data ( & message)
4360
+ }
4361
+
4362
+ impl MockProposal {
4363
+ /// The signature hash for the mock proposal
4364
+ pub fn miner_signature_hash ( & self ) -> Sha256Sum {
4365
+ let domain_tuple =
4366
+ make_structured_data_domain ( "mock-miner" , "1.0.0" , self . peer_info . network_id ) ;
4367
+ let data_tuple = Value :: Tuple (
4368
+ TupleData :: from_data ( vec ! [
4369
+ (
4370
+ "stacks-tip-consensus-hash" . into( ) ,
4371
+ Value :: buff_from( ( * self . peer_info. stacks_tip_consensus_hash. as_bytes( ) ) . into( ) )
4372
+ . unwrap( ) ,
4373
+ ) ,
4374
+ (
4375
+ "stacks-tip" . into( ) ,
4376
+ Value :: buff_from( ( * self . peer_info. stacks_tip. as_bytes( ) ) . into( ) ) . unwrap( ) ,
4377
+ ) ,
4378
+ (
4379
+ "stacks-tip-height" . into( ) ,
4380
+ Value :: UInt ( self . peer_info. stacks_tip_height. into( ) ) ,
4381
+ ) ,
4382
+ (
4383
+ "server-version" . into( ) ,
4384
+ Value :: string_ascii_from_bytes( self . peer_info. server_version. clone( ) . into( ) )
4385
+ . unwrap( ) ,
4386
+ ) ,
4387
+ (
4388
+ "pox-consensus" . into( ) ,
4389
+ Value :: buff_from( ( * self . peer_info. pox_consensus. as_bytes( ) ) . into( ) ) . unwrap( ) ,
4390
+ ) ,
4391
+ ] )
4392
+ . expect ( "Error creating signature hash" ) ,
4393
+ ) ;
4394
+ structured_data_message_hash ( data_tuple, domain_tuple)
4395
+ }
4396
+
4397
+ /// The signature hash including the miner's signature. Used by signers.
4398
+ pub fn signer_signature_hash ( & self ) -> Sha256Sum {
4399
+ let domain_tuple =
4400
+ make_structured_data_domain ( "mock-signer" , "1.0.0" , self . peer_info . network_id ) ;
4401
+ let data_tuple = Value :: Tuple (
4402
+ TupleData :: from_data ( vec ! [
4403
+ (
4404
+ "miner-signature-hash" . into( ) ,
4405
+ Value :: buff_from( ( * self . miner_signature_hash( ) . as_bytes( ) ) . into( ) ) . unwrap( ) ,
4406
+ ) ,
4407
+ (
4408
+ "miner-signature" . into( ) ,
4409
+ Value :: buff_from( ( * self . signature. as_bytes( ) ) . into( ) ) . unwrap( ) ,
4410
+ ) ,
4411
+ ] )
4412
+ . expect ( "Error creating signature hash" ) ,
4413
+ ) ;
4414
+ structured_data_message_hash ( data_tuple, domain_tuple)
4415
+ }
4320
4416
}
4321
4417
4322
4418
impl StacksMessageCodec for MockProposal {
0 commit comments