diff --git a/src/lib.rs b/src/lib.rs index fb8ad3d..7492962 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,8 +24,8 @@ mod ethsig; mod multi; mod sol; use crate::multi::CrossAsset; -use crate::sol::SubAllocSol; -use alloy_primitives::{address, keccak256, Address as EthAddress, Bytes as PrimBytes, FixedBytes, U256, U64, U16}; +use crate::sol::{get_channel_id_cross, SubAllocSol}; +use alloy_primitives::{address, keccak256, Address as EthAddress, Bytes as PrimBytes, FixedBytes, U256, U64, U16, Uint}; use soroban_sdk::xdr::WriteXdr; #[contracterror] @@ -100,7 +100,7 @@ pub struct Participant { pub struct State { /// channel_id is always a hash of the channel Params to which this /// state belongs. - pub channel_id: Vec>, + pub channel_id: BytesN<32>, /// balances is the balance distribution between the channel's participants /// in this state. pub balances: Balances, @@ -182,9 +182,9 @@ impl Adjudicator { // checks // We verify that the sha_256 hash of the params matches the channel id // in the state. - let cid = get_channel_id(&env, ¶ms); + let cid = get_channel_id_cross(&env, ¶ms); - if !channel_id_matches(&state, &cid)? { + if !cid.eq(&state.channel_id) { return Err(Error::ChannelIDMismatch); } if state.version != 0 { @@ -216,7 +216,7 @@ impl Adjudicator { // Assemble the channel. let channel = make_channel(¶ms, &state, &control); // Write the new channel to storage. - set_channel(&env, &channel, &cid); + set_channel(&env, &channel); // Emit open event. env.events().publish( (symbol_short!("perun"), symbol_short!("open")), @@ -280,7 +280,7 @@ impl Adjudicator { actor.require_auth(); // Write the updated channel to storage. - set_channel(&env, &channel, &channel_id); + set_channel(&env, &channel); // Emit fund event. env.events().publish( (symbol_short!("perun"), symbol_short!("fund")), @@ -323,7 +323,7 @@ impl Adjudicator { if !state.finalized { return Err(Error::CloseOnNonFinalState); } - let mut channel = find_valid_channel(&env, &state)?; + let mut channel = get_channel(&env, &state.channel_id).ok_or(Error::ChannelNotFound)?; // If the channel was not funded, we prohibit closing. // If we would not do this, one could drain the contract's balance // by opening a channel, closing without funding and subsequently withdrawing. @@ -332,7 +332,6 @@ impl Adjudicator { } // We verify both parties' signatures on the submitted final state. - let message = state.clone().to_xdr(&env); let state_sol_prefix_hash = hash_state_eth_prefixed(&env, &state)?; @@ -369,10 +368,10 @@ impl Adjudicator { (symbol_short!("perun"), symbol_short!("pay_c")), channel.clone(), ); - iter_delete_channel(&env, &channel)?; + delete_channel(&env, &channel.state.channel_id); } else { // Write the updated channel to contract storage. - iter_set_channel(&env, &channel)?; + set_channel(&env, &channel); } Ok(()) @@ -413,9 +412,9 @@ impl Adjudicator { (symbol_short!("perun"), symbol_short!("pay_c")), channel.clone(), ); - iter_delete_channel(&env, &channel)?; + delete_channel(&env, &channel.state.channel_id) } else { - set_channel(&env, &channel, &channel_id); + set_channel(&env, &channel); } Ok(()) @@ -430,7 +429,7 @@ impl Adjudicator { sig_b_stellar: BytesN<65>, ) -> Result<(), Error> { // checks - let mut channel = find_valid_channel(&env, &new_state)?; + let mut channel = get_channel(&env, &new_state.channel_id).ok_or(Error::ChannelNotFound)?; // We only allow dispute on funded channels. if !is_funded(&channel) { return Err(Error::OperationOnUnfundedChannel); @@ -445,8 +444,6 @@ impl Adjudicator { } // We verify that the new state is signed by both parties. - let message = new_state.clone().to_xdr(&env); - let state_sol_prefix_hash = hash_state_eth_prefixed(&env, &new_state).expect("hashing state eth style failed"); @@ -472,7 +469,7 @@ impl Adjudicator { channel.control.timestamp = env.ledger().timestamp(); // Set the new state and save the updated channel in the contract storage. channel.state = new_state; - iter_set_channel(&env, &channel)?; + set_channel(&env, &channel); // Emit a dispute event. env.events().publish( @@ -574,7 +571,7 @@ impl Adjudicator { ); delete_channel(&env, &channel_id); } else { - set_channel(&env, &channel, &channel_id); + set_channel(&env, &channel); } Ok(()) @@ -656,10 +653,10 @@ pub fn get_channel(env: &Env, id: &BytesN<32>) -> Option { /// set_channel writes the given channel to persistent storage. /// The key is the channel id in the channel's state. -pub fn set_channel(env: &Env, channel: &Channel, chan_id: &BytesN<32>) { +pub fn set_channel(env: &Env, channel: &Channel) { env.storage() .persistent() - .set(&ChannelID::ID(chan_id.clone()), channel); + .set(&ChannelID::ID(channel.state.channel_id.clone()), channel); } /// delete_channel deletes the channel with the given id from persistent storage. @@ -669,38 +666,6 @@ pub fn delete_channel(env: &Env, channel_id: &BytesN<32>) { .remove(&ChannelID::ID(channel_id.clone())); } -fn iter_delete_channel(env: &Env, channel: &Channel) -> Result<(), Error> { - let mut deletion_successful = false; - - for channel_id in channel.state.channel_id.iter() { - delete_channel(env, &channel_id); - - deletion_successful = true; - } - - if deletion_successful { - Ok(()) - } else { - Err(Error::ChannelIDMismatch) - } -} - -fn iter_set_channel(env: &Env, channel: &Channel) -> Result<(), Error> { - let mut setting_successful = false; - - for channel_id in channel.state.channel_id.iter() { - set_channel(env, channel, &channel_id); - - setting_successful = true; - } - - if setting_successful { - Ok(()) - } else { - Err(Error::ChannelNotFound) - } -} - /// get_channel_id returns the channel id for the given channel parameters. pub fn get_channel_id(env: &Env, params: &Params) -> BytesN<32> { let data = params.clone().to_xdr(env); @@ -752,24 +717,6 @@ pub fn is_valid_state_transition(old: &State, new: &State) -> bool { return true; } -pub fn channel_id_matches(state: &State, chan_id: &BytesN<32>) -> Result { - for state_channel_id in state.channel_id.iter() { - if state_channel_id == *chan_id { - return Ok(true); - } - } - Ok(false) -} - -fn find_valid_channel(env: &Env, state: &State) -> Result { - for state_channel_id in state.channel_id.iter() { - if let Some(channel) = get_channel(env, &state_channel_id) { - return Ok(channel); - } - } - Err(Error::ChannelNotFound) -} - /// A channel is considered funded, iff both funded bits are true. pub fn is_funded(channel: &Channel) -> bool { return channel.control.funded_a && channel.control.funded_b; @@ -804,18 +751,18 @@ pub fn convert_cross_assets( } let convert_asset = |cross_asset: &CrossAsset| -> Result { - let chain_id = U256::from(cross_asset.chain.as_u8()); + let chain_id = U256::from(cross_asset.chain.as_u64()); // Define zero addresses let zero_eth_address = EthAddress::from_slice(&[0u8; 20]); - let zero_stellar_address = PrimBytes::copy_from_slice(&[0u8; 40]); + let zero_stellar_address = PrimBytes::copy_from_slice(&[0u8; 32]); // Extract addresses from the CrossAsset let eth_address = &cross_asset.eth_address; let stellar_address = &cross_asset.stellar_address; // Create the holders based on the presence of addresses - let (eth_holder, cc_holder) = if stellar_address.to_xdr(e).iter().all(|byte| byte == 0u8) { + let (eth_holder, cc_holder) = if chain_id != Uint::try_from(2).unwrap() { // If there's a valid Ethereum address, use it let mut eth_addr_slice = [0u8; 20]; eth_address.copy_into_slice(&mut eth_addr_slice); @@ -852,36 +799,19 @@ pub fn convert_allocation(e: &Env, state: &State) -> Result Result Result { // let channel_id_xdr = state.channel_id.clone().to_xdr(e); - let channel_id_0 = state.channel_id.get_unchecked(0); - let channel_id_1 = state.channel_id.get_unchecked(1); + let channel_id = state.channel_id.clone(); // Define the expected length let chanid_len = 32; // Check if the length of channel_id_xdr matches the expected length - if channel_id_0.len() != chanid_len { + if channel_id.len() != chanid_len { return Err(Error::InvalidChanIdSize); // Ensure this error variant is defined } - let mut channel_id_slice_0 = [0u8; 32]; - let mut channel_id_slice_1 = [0u8; 32]; + let mut channel_id_slice = [0u8; 32]; - channel_id_0.copy_into_slice(&mut channel_id_slice_0); - channel_id_1.copy_into_slice(&mut channel_id_slice_1); + channel_id.copy_into_slice(&mut channel_id_slice); - let channel_id_alloy_0 = FixedBytes::from_slice(&channel_id_slice_0); - let channel_id_alloy_1 = FixedBytes::from_slice(&channel_id_slice_1); - let channel_id_vec = [channel_id_alloy_0, channel_id_alloy_1].to_vec(); + let channel_id_alloy = FixedBytes::from_slice(&channel_id_slice); let app_data_alloy = PrimBytes::copy_from_slice(&[]); let is_final_alloy = state.finalized; @@ -937,7 +862,7 @@ pub fn convert_state(e: &Env, state: &State) -> Result { // 1 for Ethereum, 2 for Stellar Ok(sol::StateSol { - channelID: channel_id_vec, + channelID: channel_id_alloy, version: state.version, outcome, appData: app_data_alloy, diff --git a/src/sol.rs b/src/sol.rs index b7f2c5c..2e87c41 100644 --- a/src/sol.rs +++ b/src/sol.rs @@ -23,9 +23,8 @@ use soroban_sdk::{xdr::ToXdr, BytesN, Env}; sol! { struct ParticipantSol { - address ccAddress; - bytes stellarAddress; - bytes ccPubKey; + address ethAddress; + bytes ccAddress; } struct ParamsSol { @@ -39,7 +38,7 @@ sol! { #[derive(Debug)] struct StateSol { - bytes32[] channelID; + bytes32 channelID; uint64 version; AllocationSol outcome; bytes appData; @@ -76,59 +75,109 @@ sol! { } -// Function to convert a Participant into a ParticipantSol +// convert_participant converts a Participant into a ParticipantSol pub fn convert_participant(e: &Env, participant: &Participant) -> ParticipantSol { - let stellar_addr_xdr = participant.stellar_addr.clone().to_xdr(&e); - let cc_addr_xdr = participant.cc_addr.clone().to_xdr(&e); - - let cc_pubkey_xdr = participant.stellar_pubkey.clone().to_xdr(&e); - - let mut stellar_addr_slice = [0u8; 40]; - let mut cc_addr_slice = [0u8; 28]; - let mut cc_pubkey_slice = [0u8; 104]; - - stellar_addr_xdr.copy_into_slice(&mut stellar_addr_slice); - - cc_addr_xdr.copy_into_slice(&mut cc_addr_slice); + let mut stellar_addr_prefix_slice = [0u8; 36]; + stellar_addr_prefix_slice[..4].copy_from_slice(&[0, 0, 0, 0]); - cc_pubkey_xdr.copy_into_slice(&mut cc_pubkey_slice); - - let cc_pubkey_alloy = PrimBytes::copy_from_slice(&cc_pubkey_slice); - - let stellar_addr_alloy = PrimBytes::copy_from_slice(&cc_addr_slice); - let cc_addr_alloy = Address::from_slice(&cc_addr_slice[8..28]); + let stellar_addr_xdr = participant.stellar_addr.clone().to_xdr(&e); + let stellar_pubkey_xdr = participant.stellar_pubkey.clone().to_array(); + let cc_addr = participant.cc_addr.clone(); + + let mut cc_addr_slice = [0u8; 20]; + cc_addr.copy_into_slice(&mut cc_addr_slice); + + let cc_addr_alloy = Address::from_slice(&cc_addr_slice); + let mut part_bytes = [0u8; 121]; // 65 + 36 + 20 + if stellar_addr_xdr.len() == 44 { + let mut stellar_addr_slice = [0u8; 44]; + stellar_addr_xdr.copy_into_slice(&mut stellar_addr_slice); + let stellar_addr_xdr_stripped = &stellar_addr_slice[12..]; + stellar_addr_prefix_slice[4..].copy_from_slice(stellar_addr_xdr_stripped); + + part_bytes[0..65].copy_from_slice(&stellar_pubkey_xdr); // Stellar pubkey + part_bytes[65..101].copy_from_slice(&stellar_addr_prefix_slice); // Stellar address XDR + part_bytes[101..121].copy_from_slice(&cc_addr_slice); // Cross-chain address + } else if stellar_addr_xdr.len() == 40 { + let mut stellar_addr_slice = [0u8; 40]; + stellar_addr_xdr.copy_into_slice(&mut stellar_addr_slice); + let stellar_addr_xdr_stripped = &stellar_addr_slice[8..]; + stellar_addr_prefix_slice[4..].copy_from_slice(stellar_addr_xdr_stripped); + + part_bytes[0..65].copy_from_slice(&stellar_pubkey_xdr); // Stellar pubkey + part_bytes[65..101].copy_from_slice(&stellar_addr_prefix_slice); // Stellar address XDR + part_bytes[101..121].copy_from_slice(&cc_addr_slice); // Cross-chain address + } + let stellar_addr_alloy = PrimBytes::copy_from_slice(&part_bytes); return ParticipantSol { - ccAddress: cc_addr_alloy, - stellarAddress: stellar_addr_alloy, - ccPubKey: cc_pubkey_alloy, + ethAddress: cc_addr_alloy, + ccAddress: stellar_addr_alloy, }; } +// convert_participant converts a Participant into a ParticipantSol +pub fn convert_participant1(e: &Env, participant: &Participant) -> ParticipantSol { + let mut prefixed_bytes = [0u8; 40]; + prefixed_bytes[..8].copy_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); + + let stellar_addr_xdr = participant.stellar_addr.clone().to_xdr(&e); + let cc_addr = participant.cc_addr.clone(); + + let mut cc_addr_slice = [0u8; 20]; + cc_addr.copy_into_slice(&mut cc_addr_slice); + + let cc_addr_alloy = Address::from_slice(&cc_addr_slice); + + return if stellar_addr_xdr.len() == 44 { + // AccountID is 44 bytes in XDR + let mut stellar_addr_slice = [0u8; 44]; + stellar_addr_xdr.copy_into_slice(&mut stellar_addr_slice); + let stellar_addr_xdr_stripped = &stellar_addr_slice[12..]; + prefixed_bytes[8..].copy_from_slice(stellar_addr_xdr_stripped); + let stellar_addr_alloy = PrimBytes::copy_from_slice(&prefixed_bytes); + + ParticipantSol { + ethAddress: cc_addr_alloy, + ccAddress: stellar_addr_alloy, + } + } else { + // ContractID is 40 bytes in XDR + let mut stellar_addr_slice = [0u8; 40]; + stellar_addr_xdr.copy_into_slice(&mut stellar_addr_slice); + let stellar_addr_xdr_stripped = &stellar_addr_slice[8..]; + prefixed_bytes[8..].copy_from_slice(stellar_addr_xdr_stripped); + let stellar_addr_alloy = PrimBytes::copy_from_slice(&prefixed_bytes); + ParticipantSol { + ethAddress: cc_addr_alloy, + ccAddress: stellar_addr_alloy, + } + } +} + +// Function to convert Params to ParamsSol pub fn convert_params(e: &Env, params: &Params) -> ParamsSol { + // Convert participants let part_sol_a = convert_participant(e, ¶ms.a); let part_sol_b = convert_participant(e, ¶ms.b); let participants_sol = [part_sol_a, part_sol_b].to_vec(); - let nonce_xdr = params.nonce.clone().to_xdr(e); - let mut nonce_slice = [0u8; 40]; - - nonce_xdr.copy_into_slice(&mut nonce_slice); - let nonce_array: [u8; 32] = nonce_slice[8..40] - .try_into() - .expect("Slice with incorrect length"); - let nonce_alloy = U256::from_be_bytes(nonce_array); + let nonce_bytes: [u8; 32] = params.nonce.to_array(); + let nonce_alloy = U256::from_be_bytes(nonce_bytes); + // Challenge duration as U256 let chall_duration = U256::from(params.challenge_duration); - let app_alloy = address!("0000000000000000000000000000000000000000"); - return ParamsSol { + // Default app address (assuming no app is used) + let app_alloy = Address::from_slice(&[0u8; 20]); // Null address + + ParamsSol { challengeDuration: chall_duration, nonce: nonce_alloy, participants: participants_sol, app: app_alloy, ledgerChannel: true, virtualChannel: false, - }; + } } pub fn get_channel_id_cross(e: &Env, params: &Params) -> BytesN<32> { diff --git a/src/test.rs b/src/test.rs index 83bc2a2..6f38f6d 100644 --- a/src/test.rs +++ b/src/test.rs @@ -181,24 +181,18 @@ fn test_honest_payment_cross_sameasset() { let mixed_assets = false; let mut t = setup(&env, 10, bal_a, bal_b, true, cross_chain, mixed_assets); - let stellar_channel_id = t.state.channel_id.get_unchecked(0); + let stellar_channel_id = t.state.channel_id.clone(); // We verify that the new state is signed by both parties. let message = t.params.clone().to_xdr(&env); let state_sol_prefix_hash = hash_state_eth_prefixed(&env, &t.state).expect("hashing state eth style failed"); - // println!("Encoded State: {:?}", state_sol_prefix_hash); let state_sol_prefix_hash = state_sol_prefix_hash.to_vec(); - // println!("Byte Vector: {:?}", state_sol_prefix_hash); t.client.open(&t.params, &t.state); - // println!("Params: {:?}", t.params); - // println!("State: {:?}", t.state); let sig_a_stellar = t.sigs_ccabi_a(); let sig_b_stellar = t.sigs_ccabi_b(); - // println!("SigA: {:?}", sig_a_stellar); - // println!("SigB: {:?}", sig_b_stellar); t.verify_state(&t.state, &stellar_channel_id); t.client.fund(&stellar_channel_id, &A); @@ -258,7 +252,7 @@ fn test_honest_payment_cross_mixedssets() { let mixed_assets = true; let mut t = setup(&env, 10, bal_a, bal_b, true, cross_chain, mixed_assets); - let stellar_channel_id = t.state.channel_id.get_unchecked(0); + let stellar_channel_id = t.state.channel_id.clone(); t.client.open(&t.params, &t.state); t.verify_state(&t.state, &stellar_channel_id); @@ -277,7 +271,6 @@ fn test_honest_payment_cross_mixedssets() { let sig_a_stellar = t.sigs_ccabi_a(); let sig_b_stellar = t.sigs_ccabi_b(); - t.client .close(&t.state, &sig_a_stellar, &sig_b_stellar); t.verify_state(&t.state, &stellar_channel_id); @@ -309,7 +302,7 @@ fn test_funding_abort_cross_sameasset() { let t = setup(&env, 10, bal_a, bal_b, true, cross_chain, false); - let stellar_channel_id = t.state.channel_id.get_unchecked(0); + let stellar_channel_id = t.state.channel_id.clone(); t.client.open(&t.params, &t.state); t.verify_state(&t.state, &stellar_channel_id); @@ -341,7 +334,7 @@ fn test_funding_abort_cross_mixedassets() { let t = setup(&env, 10, bal_a, bal_b, true, cross_chain, mixed_assets); - let stellar_channel_id = t.state.channel_id.get_unchecked(0); + let stellar_channel_id = t.state.channel_id.clone(); t.client.open(&t.params, &t.state); t.verify_state(&t.state, &stellar_channel_id); @@ -377,16 +370,16 @@ fn test_dispute_cross_sameasset() { let bal_contract_after_bwdraw = vec![&env, 0, 0]; let mut t = setup(&env, 10, bal_a, bal_b, true, cross_chain, false); - let stellar_channel_id = t.state.channel_id.get_unchecked(0); + let stellar_channel_id = t.state.channel_id.clone(); t.client.open(&t.params, &t.state); t.verify_state(&t.state, &stellar_channel_id); - t.client.fund(&t.state.channel_id.get_unchecked(0), &A); + t.client.fund(&t.state.channel_id, &A); t.verify_bal_contract(bal_contract_after_afund); t.verify_bal_a(bal_a_after_afund); - t.client.fund(&t.state.channel_id.get_unchecked(0), &B); + t.client.fund(&t.state.channel_id, &B); t.verify_bal_contract(bal_contract_after_bfund); t.verify_bal_b(bal_b_after_bfund); @@ -438,16 +431,16 @@ fn test_dispute_cross_mixedassets() { let bal_contract_after_bwdraw = vec![&env, 0, 0]; let mut t = setup(&env, 10, bal_a, bal_b, true, cross_chain, mixed_assets); - let stellar_channel_id = t.state.channel_id.get_unchecked(0); + let stellar_channel_id = t.state.channel_id.clone(); t.client.open(&t.params, &t.state); t.verify_state(&t.state, &stellar_channel_id); - t.client.fund(&t.state.channel_id.get_unchecked(0), &A); + t.client.fund(&t.state.channel_id, &A); t.verify_bal_contract(bal_contract_after_afund); t.verify_bal_a(bal_a_after_afund); - t.client.fund(&t.state.channel_id.get_unchecked(0), &B); + t.client.fund(&t.state.channel_id, &B); t.verify_bal_contract(bal_contract_after_bfund); t.verify_bal_b(bal_b_after_bfund); @@ -500,7 +493,7 @@ fn test_malicious_dispute() { let to_send_bal_second = vec![&env, 0, 100]; let mut t = setup(&env, 10, bal_a, bal_b, true, cross_chain, false); - let stellar_channel_id = t.state.channel_id.get_unchecked(0); + let stellar_channel_id = t.state.channel_id.clone(); t.client.open(&t.params, &t.state); t.verify_state(&t.state, &stellar_channel_id); @@ -751,16 +744,14 @@ fn setup( challenge_duration: challenge_duration, }; - let channel_id = get_channel_id(&e, ¶ms); - - let chan_ids = vec![&e, channel_id.clone(), channel_id.clone()]; + let channel_id = get_channel_id_cross(&e, ¶ms); let state = State { - channel_id: chan_ids, + channel_id: channel_id.clone(), balances: Balances { tokens: vec![&e, cross_assets_0, cross_assets_1], //vec![&e, cross_assets_1, cross_assets_2] - bal_a: bal_a, - bal_b: bal_b, + bal_a, + bal_b, }, version: 0, finalized: false,