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

Add a new transaction type that can transfer SUI coin and pay gas with it #2403

Merged
merged 4 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add TransferSui transaction type
  • Loading branch information
lxfind committed Jun 8, 2022
commit 9311a79c869b39d1e0ed9cfc70c2d2eac604cc24
66 changes: 63 additions & 3 deletions crates/sui-core/src/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ use move_core_types::language_storage::ModuleId;
use move_vm_runtime::{move_vm::MoveVM, native_functions::NativeFunctionTable};
use sui_adapter::adapter;
use sui_types::committee::EpochId;
use sui_types::error::SuiError;
use sui_types::gas_coin::GasCoin;
use sui_types::object::{MoveObject, Owner, OBJECT_START_VERSION};
use sui_types::{
base_types::{ObjectID, ObjectRef, SuiAddress, TransactionDigest, TxContext},
error::SuiResult,
event::{Event, TransferType},
fp_ensure,
gas::{self, SuiGasStatus},
messages::{
CallArg, ChangeEpoch, ExecutionStatus, MoveCall, MoveModulePublish, SingleTransactionKind,
TransactionData, TransactionEffects, TransferCoin,
TransactionData, TransactionEffects, TransferCoin, TransferSui,
},
object::Object,
storage::{BackingPackageStore, Storage},
Expand Down Expand Up @@ -113,7 +117,15 @@ fn execute_transaction<S: BackingPackageStore>(
.get(&object_ref.0)
.unwrap()
.clone();
transfer(temporary_store, object, recipient)
transfer_coin(temporary_store, object, recipient)
}
SingleTransactionKind::TransferSui(TransferSui { recipient, amount }) => {
let gas_object = temporary_store
.objects()
.get(&gas_object_id)
.expect("We constructed the object map so it should always have the gas object id")
.clone();
transfer_sui(temporary_store, gas_object, recipient, amount, tx_ctx)
}
SingleTransactionKind::Call(MoveCall {
package,
Expand Down Expand Up @@ -222,7 +234,7 @@ fn execute_transaction<S: BackingPackageStore>(
}
}

fn transfer<S>(
fn transfer_coin<S>(
temporary_store: &mut AuthorityTemporaryStore<S>,
mut object: Object,
recipient: SuiAddress,
Expand All @@ -237,3 +249,51 @@ fn transfer<S>(
temporary_store.write_object(object);
Ok(())
}

fn transfer_sui<S>(
temporary_store: &mut AuthorityTemporaryStore<S>,
mut object: Object,
recipient: SuiAddress,
amount: Option<u64>,
tx_ctx: &mut TxContext,
) -> SuiResult {
#[cfg(debug_assertions)]
let version = object.version();
if let Some(amount) = amount {
let mut gas_coin = GasCoin::try_from(&object)?;
let balance = gas_coin.value();
fp_ensure!(
balance >= amount,
SuiError::TransferInsufficientBalance {
balance,
required: amount,
}
);
let new_object = Object::new_move(
MoveObject::new(
GasCoin::type_(),
bcs::to_bytes(&GasCoin::new(
tx_ctx.fresh_id(),
OBJECT_START_VERSION,
amount,
))
.expect("Serializing gas object cannot fail"),
),
Owner::AddressOwner(recipient),
tx_ctx.digest(),
);
temporary_store.write_object(new_object);
gas_coin.0.balance.withdraw(amount);
lxfind marked this conversation as resolved.
Show resolved Hide resolved

let move_object = object.data.try_as_move_mut().expect("");
move_object.update_contents(bcs::to_bytes(&gas_coin).expect(""));
} else {
object.transfer(recipient)?;
}
let move_object = object.data.try_as_move_mut().expect("");
move_object.revert_version_increase();
debug_assert_eq!(object.version(), version);
temporary_store.write_object(object);

Ok(())
}
24 changes: 23 additions & 1 deletion crates/sui-core/src/gateway_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,8 @@ pub enum SuiTransactionKind {
Publish(SuiMovePackage),
/// Call a function in a published Move module
Call(SuiMoveCall),
/// Initiate a SUI coin transfer between addresses
TransferSui(SuiTransferSui),
/// A system transaction that will update epoch information on-chain.
ChangeEpoch(SuiChangeEpoch),
// .. more transaction types go here
Expand All @@ -830,7 +832,7 @@ impl Display for SuiTransactionKind {
let mut writer = String::new();
match &self {
Self::TransferCoin(t) => {
writeln!(writer, "Transaction Kind : Transfer")?;
writeln!(writer, "Transaction Kind : Transfer Coin")?;
writeln!(writer, "Recipient : {}", t.recipient)?;
writeln!(writer, "Object ID : {}", t.object_ref.object_id)?;
writeln!(writer, "Version : {:?}", t.object_ref.version)?;
Expand All @@ -840,6 +842,15 @@ impl Display for SuiTransactionKind {
Base64::encode(t.object_ref.digest)
)?;
}
Self::TransferSui(t) => {
writeln!(writer, "Transaction Kind : Transfer SUI")?;
writeln!(writer, "Recipient : {}", t.recipient)?;
if let Some(amount) = t.amount {
writeln!(writer, "Amount: {}", amount)?;
} else {
writeln!(writer, "Amount: Full Balance")?;
}
}
Self::Publish(_p) => {
write!(writer, "Transaction Kind : Publish")?;
}
Expand Down Expand Up @@ -875,6 +886,10 @@ impl TryFrom<SingleTransactionKind> for SuiTransactionKind {
recipient: t.recipient,
object_ref: t.object_ref.into(),
}),
SingleTransactionKind::TransferSui(t) => Self::TransferSui(SuiTransferSui {
recipient: t.recipient,
amount: t.amount,
}),
SingleTransactionKind::Publish(p) => Self::Publish(p.try_into()?),
SingleTransactionKind::Call(c) => Self::Call(SuiMoveCall {
package: c.package.into(),
Expand Down Expand Up @@ -1187,6 +1202,13 @@ pub struct SuiTransferCoin {
pub object_ref: SuiObjectRef,
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename = "TransferSui", rename_all = "camelCase")]
pub struct SuiTransferSui {
pub recipient: SuiAddress,
pub amount: Option<u64>,
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename = "InputObjectKind")]
pub enum SuiInputObjectKind {
Expand Down
4 changes: 4 additions & 0 deletions crates/sui-types/src/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ impl Balance {
}
}

pub fn withdraw(&mut self, amount: u64) {
self.value -= amount;
}

pub fn value(&self) -> u64 {
self.value
}
Expand Down
2 changes: 2 additions & 0 deletions crates/sui-types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ pub enum SuiError {
TransferNonCoinError,
#[error("A move package is expected, instead a move object is passed: {object_id}")]
MoveObjectAsPackage { object_id: ObjectID },
#[error("The SUI coin to be transferred has balance {balance}, which is not enough to cover the transfer amount {required}")]
TransferInsufficientBalance { balance: u64, required: u64 },
#[error("A move object is expected, instead a move package is passed: {object_id}")]
MovePackageAsObject { object_id: ObjectID },
#[error("Expecting a singler owner, shared ownership found")]
Expand Down
35 changes: 34 additions & 1 deletion crates/sui-types/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ pub struct MoveModulePublish {
pub modules: Vec<Vec<u8>>,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct TransferSui {
pub recipient: SuiAddress,
pub amount: Option<u64>,
}

#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct ChangeEpoch {
/// The next (to become) epoch ID.
Expand All @@ -92,6 +98,7 @@ pub enum SingleTransactionKind {
Publish(MoveModulePublish),
/// Call a function in a published Move module
Call(MoveCall),
TransferSui(TransferSui),
/// A system transaction that will update epoch information on-chain.
/// It will only ever be executed once in an epoch.
/// The argument is the next epoch number, which is critical
Expand Down Expand Up @@ -161,6 +168,9 @@ impl SingleTransactionKind {
.collect::<Vec<_>>();
Transaction::input_objects_in_compiled_modules(&compiled_modules)
}
Self::TransferSui(_) => {
vec![]
}
Self::ChangeEpoch(_) => {
vec![InputObjectKind::SharedMoveObject(
SUI_SYSTEM_STATE_OBJECT_ID,
Expand All @@ -187,13 +197,22 @@ impl Display for SingleTransactionKind {
let mut writer = String::new();
match &self {
Self::TransferCoin(t) => {
writeln!(writer, "Transaction Kind : Transfer")?;
writeln!(writer, "Transaction Kind : Transfer Coin")?;
writeln!(writer, "Recipient : {}", t.recipient)?;
let (object_id, seq, digest) = t.object_ref;
writeln!(writer, "Object ID : {}", &object_id)?;
writeln!(writer, "Sequence Number : {:?}", seq)?;
writeln!(writer, "Object Digest : {}", encode_bytes_hex(&digest.0))?;
}
Self::TransferSui(t) => {
writeln!(writer, "Transaction Kind : Transfer SUI")?;
writeln!(writer, "Recipient : {}", t.recipient)?;
if let Some(amount) = t.amount {
writeln!(writer, "Amount: {}", amount)?;
} else {
writeln!(writer, "Amount: Full Balance")?;
}
}
Self::Publish(_p) => {
writeln!(writer, "Transaction Kind : Publish")?;
}
Expand Down Expand Up @@ -336,6 +355,20 @@ where
Self::new(kind, sender, gas_payment, gas_budget)
}

pub fn new_transfer_sui(
recipient: SuiAddress,
sender: SuiAddress,
amount: Option<u64>,
gas_payment: ObjectRef,
gas_budget: u64,
) -> Self {
let kind = TransactionKind::Single(SingleTransactionKind::TransferSui(TransferSui {
recipient,
amount,
}));
Self::new(kind, sender, gas_payment, gas_budget)
}

pub fn new_module(
sender: SuiAddress,
gas_payment: ObjectRef,
Expand Down
10 changes: 10 additions & 0 deletions crates/sui-types/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ impl MoveObject {
.copy_from_slice(bcs::to_bytes(&new_version).unwrap().as_slice());
}

/// Decrease the version of this object by one.
/// This should only be called when a version is first increased, and needs to be reverted.
pub fn revert_version_increase(&mut self) {
let new_version = self.version().decrement().expect(
"Version decrement is only called after increment, and hence should never fail",
);
self.version_bytes_mut()
.copy_from_slice(bcs::to_bytes(&new_version).unwrap().as_slice());
}

fn version_bytes(&self) -> &BcsU64 {
self.contents[ID_END_INDEX..VERSION_END_INDEX]
.try_into()
Expand Down