#Serialize Struct

22 messages · Page 1 of 1 (latest)

rancid shadow
#

I am new in soroban. so pardon me if I am asking something wrong.
I have a struct that I need to serialize and creat a hash out of it. Here is how struct looks like

pub struct Message {
    version: u8,
    nonce: u32,
    sender: Address,
    recipient: Address,
    body: Bytes,
}

Now I am planning to create a ID for any message using these fields. So my idea is to serialize the struct and then create a hash out of it.
Is there any way to do it ? Or am I thinking in wrong way ?
Is there any better way to generate message id that is always deterministic based on given message data ?

ocean shard
#

You can make a deterministic address by deploying a contract with a known salt. So maybe theres a way to just generate the address without actually deploying a contract?

Should be possible https://github.com/stellar/rs-soroban-env/pull/839/commits/e368535ac4a22e76b1df9f50c6255b18482cdff6

GitHub

This adds host functions for secp256k1 (#684) and keccak256 (#676)
Task list:

decide whether to include one or more of the SHA3-as-standardized variants, or just keccak256
decide whether to use ...

#

let admin_raw = Val::try_from_val(&env, &admin).unwrap();

#

You can convert each item into a rawval like so

#

Then concatenate those together

#

Might need to use alloc. Im half asleep but it seems like itd work

rancid shadow
#

Thanks @ocean shard , can we also convert address. to rawval ?

ocean shard
#

Disclaimer this might not be the best way to do what you want but something along these lines should work. Not sure if that .concat method will work

use soroban_sdk::{contract, contractimpl, Address, BytesN, Env, Val, TryFromVal};

pub struct Message {
    version: u8,
    nonce: u32,
    sender: Address,
    recipient: Address,
    body: Bytes,
}

impl Message {
    /// Serializes the Message using Val::try_from_val and then hashes it.
    pub fn generate_id(&self, env: &Env) -> BytesN<32> {
        // Convert each field of the Message struct into Val and then serialize them into bytes.
        let version_val = Val::try_from_val(&env, &self.version).unwrap();
        let nonce_val = Val::try_from_val(&env, &self.nonce).unwrap();
        let sender_val = Val::try_from_val(&env, &self.sender).unwrap();
        let recipient_val = Val::try_from_val(&env, &self.recipient).unwrap();
        let body_val = Val::try_from_val(&env, &self.body).unwrap();

        // Combine the serialized bytes of each field to get the complete serialized Message.
        let serialized = [
            version_val.as_bytes().as_ref(),
            nonce_val.as_bytes().as_ref(),
            sender_val.as_bytes().as_ref(),
            recipient_val.as_bytes().as_ref(),
            body_val.as_bytes().as_ref()
        ].concat();

        // Convert the serialized data into Bytes
        let data = Bytes::new(&env, &serialized);

        // Hash the serialized Message using SHA-256
        let crypto = Crypto::new(&env);
        crypto.sha256(&data)
    }
}```
#

Theres also kecceck if you dont want sha256

#

Im not sure if the way i made the serialized variable will work or not ive not tried that

#

Maybe this woild work better for serialized on my phone so cant test.

            version_val.to_object(), 
            nonce_val.to_object(), 
            sender_val.to_object(), 
            recipient_val.to_object(), 
            body_val.to_object()
        );```
#

Do me a favor and let me know if u get it to work

rancid shadow
#

this is really helpful @ocean shard thanks a lot for giving some time to write code 🙏🏻

ocean shard
#

Hopefully it helps but i have no idea if it will work or not. Good luck.

ornate jolt
#

I don't know how this is in terms of efficiency but I would try something like that to serialize and hash.

#[contracttype]
pub struct Message {
    version: u32,// cannot use u8 with contracttype
    nonce: u32,
    sender: Address,
    recipient: Address,
    body: Bytes,
}

let message: Message = Message { version: 0, nonce: 0, sender: a, recipient: b, body: bytes!(&env, 0x0000000000) };
let serialized_message = message.to_xdr(&env);
let hashed = env.crypto().sha256(&serialized_message);
ocean shard
#

Looks easier.

#

Didnt think of that. Seems like it should work

rancid shadow
#

awesome @ornate jolt for providing the example. I didn't know to_xdr gives bytes in return. what does xdr stands for ?

ornate jolt
rancid shadow
#

@ornate jolt it says to_xdr method not found in Message struct, does that mean I need to impl that method manually ?

ornate jolt
#

you have to add #[contracttype] before Message declaration,
change version: u8 to something supported like u32
and import use soroban_sdk::xdr::ToXdr

rancid shadow