Skip to content
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
6 changes: 3 additions & 3 deletions lazer/contracts/sui/sources/i16.move
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public fun get_magnitude_if_negative(in: &I16): u64 {

public fun from_u16(from: u16): I16 {
// Use the MSB to determine whether the number is negative or not.
let from_u64 = (from as u64);
let from_u64 = from as u64;
let negative = (from_u64 >> 15) == 1;
let magnitude = parse_magnitude(from_u64, negative);

Expand All @@ -71,7 +71,7 @@ fun parse_magnitude(from: u64, negative: bool): u64 {
#[test]
fun test_max_positive_magnitude() {
new(0x7FFF, false); // 32767
assert!(&new((1<<15) - 1, false) == &from_u16(((1<<15) - 1) as u16), 1);
assert!(&new((1 << 15) - 1, false) == &from_u16((1 << 15) - 1), 1);
}

#[test]
Expand All @@ -83,7 +83,7 @@ fun test_magnitude_too_large_positive() {
#[test]
fun test_max_negative_magnitude() {
new(0x8000, true); // 32768
assert!(&new(1<<15, true) == &from_u16((1<<15) as u16), 1);
assert!(&new(1 << 15, true) == &from_u16(1 << 15), 1);
}

#[test]
Expand Down
4 changes: 2 additions & 2 deletions lazer/contracts/sui/sources/i64.move
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ fun parse_magnitude(from: u64, negative: bool): u64 {
#[test]
fun test_max_positive_magnitude() {
new(0x7FFFFFFFFFFFFFFF, false);
assert!(&new(1<<63 - 1, false) == &from_u64(1<<63 - 1), 1);
assert!(&new((1 << 63) - 1, false) == &from_u64((1 << 63) - 1), 1);
}

#[test]
Expand All @@ -82,7 +82,7 @@ fun test_magnitude_too_large_positive() {
#[test]
fun test_max_negative_magnitude() {
new(0x8000000000000000, true);
assert!(&new(1<<63, true) == &from_u64(1<<63), 1);
assert!(&new(1 << 63, true) == &from_u64(1 << 63), 1);
}

#[test]
Expand Down
10 changes: 5 additions & 5 deletions lazer/contracts/sui/sources/pyth_lazer.move
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,14 @@ public(package) fun verify_le_ecdsa_message(
let pubkey = secp256k1_ecrecover(signature, payload, 0);

// Check if the recovered pubkey is in the trusted signers list
let trusted_signers = state::get_trusted_signers(s);
let trusted_signers = s.get_trusted_signers();
let mut maybe_idx = state::find_signer_index(trusted_signers, &pubkey);

if (option::is_some(&maybe_idx)) {
let idx = option::extract(&mut maybe_idx);
let found_signer = &trusted_signers[idx];
let expires_at = state::expires_at(found_signer);
assert!(clock.timestamp_ms() < expires_at, ESignerExpired);
let expires_at_ms = found_signer.expires_at_ms();
assert!(clock.timestamp_ms() < expires_at_ms, ESignerExpired);
} else {
abort ESignerNotTrusted
}
Expand Down Expand Up @@ -100,8 +100,8 @@ public fun parse_and_verify_le_ecdsa_update(s: &State, clock: &Clock, update: ve
let payload_len = cursor.peel_u16();
let payload = cursor.into_remainder_bytes();

// Validate expectedpayload length
assert!((payload_len as u64) == payload.length(), EInvalidPayloadLength);
// Validate expected payload length
assert!(payload_len as u64 == payload.length(), EInvalidPayloadLength);

// Parse payload
let mut payload_cursor = bcs::new(payload);
Expand Down
104 changes: 50 additions & 54 deletions lazer/contracts/sui/sources/state.move
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module pyth_lazer::state;

use pyth_lazer::admin::{Self, AdminCap};
#[test_only]
use pyth_lazer::admin;
use pyth_lazer::admin::AdminCap;

const SECP256K1_COMPRESSED_PUBKEY_LEN: u64 = 33;
const EInvalidPubkeyLen: u64 = 1;
Expand Down Expand Up @@ -35,9 +37,9 @@ public fun public_key(info: &TrustedSignerInfo): &vector<u8> {
&info.public_key
}

/// Get the trusted signer's expiry timestamp (seconds since Unix epoch)
public fun expires_at(info: &TrustedSignerInfo): u64 {
info.expires_at
/// Get the trusted signer's expiry timestamp, converted to milliseconds
public fun expires_at_ms(info: &TrustedSignerInfo): u64 {
info.expires_at * 1000
}

/// Get the list of trusted signers
Expand All @@ -50,43 +52,42 @@ public fun get_trusted_signers(s: &State): &vector<TrustedSignerInfo> {
/// - If the expired_at is set to zero, the trusted signer will be removed.
/// - If the pubkey isn't found, it is added as a new trusted signer with the given expires_at.
public fun update_trusted_signer(_: &AdminCap, s: &mut State, pubkey: vector<u8>, expires_at: u64) {
assert!(vector::length(&pubkey) as u64 == SECP256K1_COMPRESSED_PUBKEY_LEN, EInvalidPubkeyLen);
assert!(pubkey.length() == SECP256K1_COMPRESSED_PUBKEY_LEN, EInvalidPubkeyLen);

let mut maybe_idx = find_signer_index(&s.trusted_signers, &pubkey);
if (expires_at == 0) {
if (option::is_some(&maybe_idx)) {
let idx = option::extract(&mut maybe_idx);
if (maybe_idx.is_some()) {
let idx = maybe_idx.extract();
// Remove by swapping with last (order not preserved), discard removed value
let _ = vector::swap_remove(&mut s.trusted_signers, idx);
let _ = s.trusted_signers.swap_remove(idx);
} else {
option::destroy_none(maybe_idx);
maybe_idx.destroy_none();
abort ESignerNotFound
};
return
};

if (option::is_some(&maybe_idx)) {
let idx = option::extract(&mut maybe_idx);
let info_ref = vector::borrow_mut(&mut s.trusted_signers, idx);
if (maybe_idx.is_some()) {
let idx = maybe_idx.extract();
let info_ref = &mut s.trusted_signers[idx];
info_ref.expires_at = expires_at
} else {
option::destroy_none(maybe_idx);
vector::push_back(
&mut s.trusted_signers,
TrustedSignerInfo { public_key: pubkey, expires_at },
)
maybe_idx.destroy_none();
s.trusted_signers.push_back(
TrustedSignerInfo { public_key: pubkey, expires_at }
);
}
}

public fun find_signer_index(
signers: &vector<TrustedSignerInfo>,
public_key: &vector<u8>,
): Option<u64> {
let len = vector::length(signers);
let len = signers.length();
let mut i: u64 = 0;
while (i < (len as u64)) {
let info_ref = vector::borrow(signers, i);
if (*public_key(info_ref) == *public_key) {
while (i < len) {
let signer = &signers[i];
if (signer.public_key() == public_key) {
return option::some(i)
};
i = i + 1
Expand All @@ -106,7 +107,7 @@ public fun new_for_test(ctx: &mut TxContext): State {
public fun destroy_for_test(s: State) {
let State { id, trusted_signers } = s;
let _ = trusted_signers;
object::delete(id);
id.delete();
}

#[test]
Expand All @@ -120,16 +121,15 @@ public fun test_add_new_signer() {

update_trusted_signer(&admin_cap, &mut s, pk, expiry);

let signers_ref = get_trusted_signers(&s);
assert!(vector::length(signers_ref) == 1, 100);
let info = vector::borrow(signers_ref, 0);
assert!(expires_at(info) == 123, 101);
let got_pk = public_key(info);
assert!(vector::length(got_pk) == (SECP256K1_COMPRESSED_PUBKEY_LEN as u64), 102);
let State { id, trusted_signers } = s;
let _ = trusted_signers;
object::delete(id);
admin::destroy_for_test(admin_cap);
let signers_ref = s.get_trusted_signers();
assert!(signers_ref.length() == 1, 100);
let info = &signers_ref[0];
assert!(info.expires_at == 123, 101);
let got_pk = info.public_key();
assert!(got_pk.length() == SECP256K1_COMPRESSED_PUBKEY_LEN, 102);

s.destroy_for_test();
admin_cap.destroy_for_test();
}

#[test]
Expand All @@ -151,14 +151,13 @@ public fun test_update_existing_signer_expiry() {
2000,
);

let signers_ref = get_trusted_signers(&s);
assert!(vector::length(signers_ref) == 1, 110);
let info = vector::borrow(signers_ref, 0);
assert!(expires_at(info) == 2000, 111);
let State { id, trusted_signers } = s;
let _ = trusted_signers;
object::delete(id);
admin::destroy_for_test(admin_cap);
let signers_ref = s.get_trusted_signers();
assert!(signers_ref.length() == 1, 110);
let info = &signers_ref[0];
assert!(info.expires_at == 2000, 111);

s.destroy_for_test();
admin_cap.destroy_for_test();
}

#[test]
Expand All @@ -180,12 +179,11 @@ public fun test_remove_signer_by_zero_expiry() {
0,
);

let signers_ref = get_trusted_signers(&s);
assert!(vector::length(signers_ref) == 0, 120);
let State { id, trusted_signers } = s;
let _ = trusted_signers;
object::delete(id);
admin::destroy_for_test(admin_cap);
let signers_ref = s.get_trusted_signers();
assert!(signers_ref.length() == 0, 120);

s.destroy_for_test();
admin_cap.destroy_for_test();
}

#[test, expected_failure(abort_code = EInvalidPubkeyLen)]
Expand All @@ -196,10 +194,9 @@ public fun test_invalid_pubkey_length_rejected() {

let short_pk = x"010203";
update_trusted_signer(&admin_cap, &mut s, short_pk, 1);
let State { id, trusted_signers } = s;
let _ = trusted_signers;
object::delete(id);
admin::destroy_for_test(admin_cap);

s.destroy_for_test();
admin_cap.destroy_for_test();
}

#[test, expected_failure(abort_code = ESignerNotFound)]
Expand All @@ -215,8 +212,7 @@ public fun test_remove_nonexistent_signer_fails() {
x"03aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
0,
);
let State { id, trusted_signers } = s;
let _ = trusted_signers;
object::delete(id);
admin::destroy_for_test(admin_cap);

s.destroy_for_test();
admin_cap.destroy_for_test();
}
17 changes: 11 additions & 6 deletions lazer/contracts/sui/sources/update.move
Original file line number Diff line number Diff line change
Expand Up @@ -27,35 +27,40 @@ public fun channel(update: &Update): Channel {
update.channel
}

/// Get a reference to the feeds vector of the update
/// Get a copy of the feeds vector of the update
public fun feeds(update: &Update): vector<Feed> {
update.feeds
}

/// Get a reference to the feeds vector of the update
public fun feeds_ref(update: &Update): &vector<Feed> {
&update.feeds
}

/// Parse the update from a BCS cursor containing the payload data
/// This assumes the payload magic has already been validated and consumed
public(package) fun parse_from_cursor(mut cursor: bcs::BCS): Update {
// Parse timestamp
let timestamp = cursor.peel_u64();

// Parse channel
let channel_value = cursor.peel_u8();
let channel = channel::from_u8(channel_value);

// Parse feeds
let feed_count = cursor.peel_u8();
let mut feeds = vector::empty<Feed>();
let mut feed_i = 0;

while (feed_i < feed_count) {
let feed = feed::parse_from_cursor(&mut cursor);
vector::push_back(&mut feeds, feed);
feed_i = feed_i + 1;
};

// Verify no remaining bytes
let remaining_bytes = cursor.into_remainder_bytes();
assert!(remaining_bytes.length() == 0, EInvalidPayload);

Update { timestamp, channel, feeds }
}
Loading