Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solana: align with keccak hashing #463

Merged
merged 1 commit into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anchor_lang::solana_program::hash;
use anchor_lang::solana_program::keccak;

pub const LEAF_DOMAIN_SEPARATOR: [u8; 32] = [0; 32];
const MAX_NUM_HASHES: usize = 128; // TODO: Change this to 256 when supporting commit reports with 256 messages
Expand All @@ -10,9 +10,9 @@ pub enum MerkleError {

fn hash_pair(hash1: &[u8; 32], hash2: &[u8; 32]) -> [u8; 32] {
if hash1 < hash2 {
hash::hashv(&[hash1, hash2]).to_bytes()
keccak::hashv(&[hash1, hash2]).to_bytes()
} else {
hash::hashv(&[hash2, hash1]).to_bytes()
keccak::hashv(&[hash2, hash1]).to_bytes()
}
}

Expand Down Expand Up @@ -58,15 +58,15 @@ mod tests {
.unwrap(),
];
let expected_root: [u8; 32] =
hex::decode("d23481665c993b84af89b0fec64bc789ba1d39b97e2e947f550e69a0eef3cf5c")
hex::decode("94b949ca8fd6307aa72481fe44eca36c63686f8e85acac99e4f0cd2b36a99d33")
.unwrap()
.to_owned()
.try_into()
.unwrap();

let result = calculate_merkle_root(hashed_leaf, proofs);
assert!(result.is_ok());
assert_eq!(expected_root, result.unwrap());
assert_eq!(hex::encode(expected_root), hex::encode(result.unwrap()));
}

#[test]
Expand All @@ -88,7 +88,7 @@ mod tests {

let result = calculate_merkle_root(hashed_leaf, proofs);
assert!(result.is_ok());
assert_eq!(expected_root, result.unwrap());
assert_eq!(hex::encode(expected_root), hex::encode(result.unwrap()));
}

#[test]
Expand Down Expand Up @@ -126,14 +126,14 @@ mod tests {
];

let expected_root: [u8; 32] =
hex::decode("00e9612b8588dc36a210ac439ba6569ca5263a98b3f9c2da5b342dc7925d3393")
hex::decode("577252413aa3c3c02bca5a8e30ad69fdf1b138d4ccc3d834d3c6934775ceaf87")
.unwrap()
.to_owned()
.try_into()
.unwrap();

let result = calculate_merkle_root(a, proofs);
assert!(result.is_ok());
assert_eq!(expected_root, result.unwrap());
assert_eq!(hex::encode(expected_root), hex::encode(result.unwrap()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ pub(super) struct Ocr3ReportForCommit<'a>(pub &'a CommitInput);

impl Ocr3Report for Ocr3ReportForCommit<'_> {
fn hash(&self, ctx: &ReportContext) -> [u8; 32] {
use anchor_lang::solana_program::hash;
use anchor_lang::solana_program::keccak;
let mut buffer: Vec<u8> = Vec::new();
self.0.serialize(&mut buffer).unwrap();
let report_len = self.len() as u16; // u16 > max tx size, u8 may have overflow
hash::hashv(&[&report_len.to_le_bytes(), &buffer, &ctx.as_bytes()]).to_bytes()
keccak::hashv(&[&report_len.to_le_bytes(), &buffer, &ctx.as_bytes()]).to_bytes()
}

fn len(&self) -> usize {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ pub fn validate_execution_report<'info>(
}

fn hash(msg: &Any2SVMRampMessage) -> [u8; 32] {
use anchor_lang::solana_program::hash;
use anchor_lang::solana_program::keccak;

// Calculate vectors size to ensure that the hash is unique
let sender_size = [msg.sender.len() as u8];
Expand All @@ -684,9 +684,8 @@ fn hash(msg: &Any2SVMRampMessage) -> [u8; 32] {
let header_sequence_number = msg.header.sequence_number.to_be_bytes();
let header_nonce = msg.header.nonce.to_be_bytes();

// NOTE: calling hash::hashv is orders of magnitude cheaper than using Hasher::hashv
// As similar as https://github.com/smartcontractkit/chainlink/blob/d1a9f8be2f222ea30bdf7182aaa6428bfa605cf7/contracts/src/v0.8/ccip/libraries/Internal.sol#L111
let result = hash::hashv(&[
let result = keccak::hashv(&[
LEAF_DOMAIN_SEPARATOR.as_slice(),
// metadata hash
"Any2SVMMessageHashV1".as_bytes(),
Expand Down Expand Up @@ -887,7 +886,7 @@ mod tests {
let hash_result = hash(&message);

assert_eq!(
"266b8d99e64a52fdd325f67674f56d0005dbee5e9999ff22017d5b117fbedfa3",
"4db316059ebdabdb76b8b090d4df866c00de34c4f1ab959fc3ad142c8bde3bfa",
hex::encode(hash_result)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ fn bump_nonce(nonce_counter_account: &mut Account<Nonce>, extra_args: AnyExtraAr
}

fn hash(msg: &SVM2AnyRampMessage) -> [u8; 32] {
use anchor_lang::solana_program::hash;
use anchor_lang::solana_program::keccak;

// Push Data Size to ensure that the hash is unique
let data_size = msg.data.len() as u16; // u16 > maximum transaction size, u8 may have overflow
Expand All @@ -357,9 +357,8 @@ fn hash(msg: &SVM2AnyRampMessage) -> [u8; 32] {
let header_sequence_number = msg.header.sequence_number.to_be_bytes();
let header_nonce = msg.header.nonce.to_be_bytes();

// NOTE: calling hash::hashv is orders of magnitude cheaper than using Hasher::hashv
// similar to: https://github.com/smartcontractkit/chainlink/blob/d1a9f8be2f222ea30bdf7182aaa6428bfa605cf7/contracts/src/v0.8/ccip/libraries/Internal.sol#L134
let result = hash::hashv(&[
let result = keccak::hashv(&[
LEAF_DOMAIN_SEPARATOR.as_slice(),
// metadata
"SVM2AnyMessageHashV1".as_bytes(),
Expand Down Expand Up @@ -514,7 +513,7 @@ mod tests {
let hash_result = hash(&message);

assert_eq!(
"877ba2a7329fe40e5f73b697eff78577988a72216e6c96b57335c97f92e14268",
"009bc51872fe41ea096bd881bf52e3daf07c80e112ffeeba6aa503d8281b6bfd",
hex::encode(hash_result)
);
}
Expand Down
10 changes: 5 additions & 5 deletions chains/solana/utils/ccip/ccip_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package ccip
import (
"bytes"
"context"
"crypto/sha256"
"encoding/binary"
"encoding/hex"

bin "github.com/gagliardetto/binary"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
"golang.org/x/crypto/sha3"

"github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config"
"github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router"
Expand All @@ -19,7 +19,7 @@ import (
var leafDomainSeparator = [32]byte{}

func HashCommitReport(ctx [3][32]byte, report ccip_router.CommitInput) ([]byte, error) {
hash := sha256.New()
hash := sha3.NewLegacyKeccak256()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why legacy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's the one that MCMS also uses - the sha3 package only exports keccak hashing via the legacy function

https://pkg.go.dev/golang.org/x/crypto/sha3#NewLegacyKeccak256

encodedReport, err := bin.MarshalBorsh(report)
if err != nil {
return nil, err
Expand Down Expand Up @@ -127,7 +127,7 @@ func MakeAnyToSVMMessage(tokenReceiver solana.PublicKey, logicReceiver solana.Pu
}

func HashAnyToSVMMessage(msg ccip_router.Any2SVMRampMessage, onRampAddress []byte) ([]byte, error) {
hash := sha256.New()
hash := sha3.NewLegacyKeccak256()

hash.Write(leafDomainSeparator[:])
hash.Write([]byte("Any2SVMMessageHashV1"))
Expand Down Expand Up @@ -195,7 +195,7 @@ func HashAnyToSVMMessage(msg ccip_router.Any2SVMRampMessage, onRampAddress []byt

// hashPair hashes two byte slices and returns the result as a byte slice.
func hashPair(a, b []byte) []byte {
h := sha256.New()
h := sha3.NewLegacyKeccak256()
if bytes.Compare(a, b) < 0 {
h.Write(a)
h.Write(b)
Expand All @@ -222,7 +222,7 @@ func MerkleFrom(data [][]byte) []byte {
}

func HashSVMToAnyMessage(msg ccip_router.SVM2AnyRampMessage) ([]byte, error) {
hash := sha256.New()
hash := sha3.NewLegacyKeccak256()

hash.Write(leafDomainSeparator[:])
hash.Write([]byte("SVM2AnyMessageHashV1"))
Expand Down
4 changes: 2 additions & 2 deletions chains/solana/utils/ccip/ccip_messages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func TestMessageHashing(t *testing.T) {
}, config.OnRampAddress)

require.NoError(t, err)
require.Equal(t, "266b8d99e64a52fdd325f67674f56d0005dbee5e9999ff22017d5b117fbedfa3", hex.EncodeToString(h))
require.Equal(t, "4db316059ebdabdb76b8b090d4df866c00de34c4f1ab959fc3ad142c8bde3bfa", hex.EncodeToString(h))
})

t.Run("SVMToAny", func(t *testing.T) {
Expand Down Expand Up @@ -91,6 +91,6 @@ func TestMessageHashing(t *testing.T) {
},
})
require.NoError(t, err)
require.Equal(t, "877ba2a7329fe40e5f73b697eff78577988a72216e6c96b57335c97f92e14268", hex.EncodeToString(h))
require.Equal(t, "009bc51872fe41ea096bd881bf52e3daf07c80e112ffeeba6aa503d8281b6bfd", hex.EncodeToString(h))
})
}
Loading