Skip to content

Commit

Permalink
Implemented asset modification
Browse files Browse the repository at this point in the history
  • Loading branch information
gevorgvoskanyan committed Feb 14, 2025
1 parent fd77217 commit 8dee0d6
Show file tree
Hide file tree
Showing 22 changed files with 784 additions and 54 deletions.
10 changes: 9 additions & 1 deletion src/primitives/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,17 @@ bool CTransaction::IsSpatsUnregister() const
return false;
}

bool CTransaction::IsSpatsModify() const
{
for (const CTxOut &txout: vout)
if (txout.scriptPubKey.IsSpatsModify())
return true;
return false;
}

bool CTransaction::IsSpatsTransaction() const
{
return IsSpatsCreate() || IsSpatsUnregister(); // TODO more
return IsSpatsCreate() || IsSpatsUnregister() || IsSpatsModify(); // TODO more
}

bool CTransaction::IsSparkSpend() const
Expand Down
1 change: 1 addition & 0 deletions src/primitives/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ class CTransaction
bool IsSpatsTransaction() const;
bool IsSpatsCreate() const;
bool IsSpatsUnregister() const;
bool IsSpatsModify() const;

bool HasNoRegularInputs() const;
bool HasPrivateInputs() const;
Expand Down
23 changes: 23 additions & 0 deletions src/qt/myownspats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ MyOwnSpats::MyOwnSpats( const PlatformStyle *platform_style, QWidget *parent )
ui_->tableWidgetMyOwnSpats->setSelectionBehavior( QAbstractItemView::SelectRows );
ui_->tableWidgetMyOwnSpats->setSelectionMode( QAbstractItemView::SingleSelection );
connect( ui_->create_spark_asset, &QPushButton::clicked, this, &MyOwnSpats::onCreateButtonClicked );
connect( ui_->modify_spark_asset, &QPushButton::clicked, this, &MyOwnSpats::onModifyButtonClicked );
connect( ui_->unregister_spark_asset, &QPushButton::clicked, this, &MyOwnSpats::onUnregisterButtonClicked );
connect( this, &MyOwnSpats::displayMyOwnSpatsSignal, this, &MyOwnSpats::handleDisplayMyOwnSpatsSignal );
connect( ui_->tableWidgetMyOwnSpats->selectionModel(), &QItemSelectionModel::selectionChanged, this, &MyOwnSpats::updateButtonStates );
Expand Down Expand Up @@ -145,6 +146,28 @@ void MyOwnSpats::onCreateButtonClicked()
}
}

void MyOwnSpats::onModifyButtonClicked()
{
assert( wallet_model_ );
if ( const auto row = get_the_selected_row() ) {
try {
const spats::asset_type_t asset_type{ ui_->tableWidgetMyOwnSpats->item( *row, 0 )->text().toULongLong() };
spats::identifier_t identifier{ 0 };
if ( !is_fungible_asset_type( asset_type ) )
identifier = spats::identifier_t{ ui_->tableWidgetMyOwnSpats->item( *row, 1 )->text().toULongLong() };
const auto &existing_asset = my_own_assets_map_.at( spats::universal_asset_id_t{ asset_type, identifier } );
SparkAssetDialog dialog( platform_style_, existing_asset, this );
if ( dialog.exec() == QDialog::Accepted )
wallet_model_->getWallet()->ModifySparkAsset( existing_asset, *dialog.getResultAsset() ); // TODO user confirm callback
}
catch ( const std::exception &e ) {
QMessageBox::critical( this, tr( "Error" ), tr( "An error occurred: %1" ).arg( e.what() ) );
}
}
else
QMessageBox::critical( this, tr( "Error" ), tr( "Please select an asset to modify." ) );
}

void MyOwnSpats::onUnregisterButtonClicked()
{
assert( wallet_model_ );
Expand Down
1 change: 1 addition & 0 deletions src/qt/myownspats.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class MyOwnSpats : public QWidget, public spats::UpdatesObserver {

private Q_SLOTS:
void onCreateButtonClicked();
void onModifyButtonClicked();
void onUnregisterButtonClicked();
void handleDisplayMyOwnSpatsSignal() { display_my_own_spats(); }
void updateButtonStates();
Expand Down
27 changes: 12 additions & 15 deletions src/qt/sparkassetdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,13 @@ SparkAssetDialog::SparkAssetDialog( const PlatformStyle *platform_style, dialog_
// Example: Apply styles (if applicable to your PlatformStyle)
}

std::visit( [ this ]( auto &&arg ) { set_fields( arg ); }, context_ );

// Connect UI elements to actions
connect( ui_->fungibilityCheckBox, &QCheckBox::stateChanged, this, &SparkAssetDialog::onFungibilityChanged );
connect( ui_->assetTypeSpinBox, &QUInt64SpinBox::valueChanged, this, &SparkAssetDialog::onAssetTypeChanged );
connect( ui_->saveButton, &QPushButton::clicked, this, &SparkAssetDialog::onSave );
connect( ui_->cancelButton, &QPushButton::clicked, this, &QDialog::reject );

std::visit( [ this ]( auto &&arg ) { set_fields( arg ); }, context_ );

// Update field visibility/state based on fungibility checkbox state
onFungibilityChanged( ui_->fungibilityCheckBox->isChecked() ? Qt::Checked : Qt::Unchecked );
}

SparkAssetDialog::~SparkAssetDialog() {}
Expand Down Expand Up @@ -113,17 +110,17 @@ void SparkAssetDialog::onSave()

void SparkAssetDialog::onFungibilityChanged( int state )
{
const bool is_fungible = ( state == Qt::Checked );
if ( const auto *const context = std::get_if< NewSparkAssetCreationContext >( &context_ ) ) {
const bool is_fungible = ( state == Qt::Checked );

// Enable or disable fungible-related fields
ui_->totalSupplySpin->setEnabled( is_fungible );
ui_->resupplyableCheckBox->setEnabled( is_fungible );
ui_->precisionSpinBox->setEnabled( is_fungible );
// Enable or disable fungible-related fields
ui_->totalSupplySpin->setEnabled( is_fungible );
ui_->resupplyableCheckBox->setEnabled( is_fungible );
ui_->precisionSpinBox->setEnabled( is_fungible );

// Non-Fungible-specific fields
ui_->identifierSpinBox->setEnabled( !is_fungible );
// Non-Fungible-specific fields
ui_->identifierSpinBox->setEnabled( !is_fungible );

if ( const auto *const context = std::get_if< NewSparkAssetCreationContext >( &context_ ) ) {
if ( is_fungible ) {
ui_->assetTypeSpinBox->setValue( context->lowest_available_asset_type_for_new_fungible_asset );
}
Expand All @@ -132,6 +129,8 @@ void SparkAssetDialog::onFungibilityChanged( int state )
}
onAssetTypeChanged( ui_->assetTypeSpinBox->value() );
}
else
assert( !"Not allowed to modify the fungibility of an existing asset" );
}

void SparkAssetDialog::onAssetTypeChanged( int asset_type_value )
Expand Down Expand Up @@ -191,8 +190,6 @@ void SparkAssetDialog::set_fields( const spats::SparkAsset &existing_asset )
} },
existing_asset );

onFungibilityChanged( ui_->fungibilityCheckBox->checkState() );

ui_->fungibilityCheckBox->setEnabled( false );
ui_->assetTypeSpinBox->setEnabled( false );
ui_->identifierSpinBox->setEnabled( false );
Expand Down
6 changes: 6 additions & 0 deletions src/script/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ const char* GetOpName(opcodetype opcode)
case OP_SPARKSPEND : return "OP_SPARKSPEND";
case OP_SPATSCREATE : return "OP_SPATSCREATE";
case OP_SPATSUNREGISTER : return "OP_SPATSUNREGISTER";
case OP_SPATSMODIFY : return "OP_SPATSMODIFY";
// Super transparent txout script prefix
case OP_EXCHANGEADDR : return "OP_EXCHANGEADDR";

Expand Down Expand Up @@ -371,6 +372,11 @@ bool CScript::IsSpatsUnregister() const
return this->size() > 0 && (*this)[0] == OP_SPATSUNREGISTER;
}

bool CScript::IsSpatsModify() const
{
return this->size() > 0 && (*this)[0] == OP_SPATSMODIFY;
}

bool CScript::IsSpats() const
{
return this->size() > 0 && IsSpatsOp(static_cast<opcodetype>((*this)[0]));
Expand Down
8 changes: 7 additions & 1 deletion src/script/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,16 @@ enum opcodetype
OP_SPARKSPEND = 0xd3,
OP_SPATSCREATE = 0xd4,
OP_SPATSUNREGISTER = 0xd5,
OP_SPATSMODIFY = 0xd6,
// TODO when adding a new spats opcode, update this below, and keep all spats ops values consecutive if possible, otherwise change IsSpatsOp() implementation
OP_SPATSLAST = OP_SPATSUNREGISTER,
OP_SPATSLAST = OP_SPATSMODIFY,

// basically NOP but identifies that subsequent txout script contains super transparent address
OP_EXCHANGEADDR = 0xe0
};
// ATTENTION: When adding a new enumerator to the above, make sure to update the src/test/data/script_tests.json file accordingly, e.g. by deleting the corresponding
// ["0", "IF 0xNN ELSE 1 ENDIF", "P2SH,STRICTENC", "OK"],
// line...

const char* GetOpName(opcodetype opcode);

Expand Down Expand Up @@ -702,6 +706,8 @@ class CScript : public CScriptBase

bool IsSpatsUnregister() const;

bool IsSpatsModify() const;

bool IsSpats() const;

bool IsZerocoinRemint() const;
Expand Down
38 changes: 38 additions & 0 deletions src/spark/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,51 @@ static spats::UnregisterAssetAction ParseSpatsUnregisterTransaction(const CTrans
}
}

static spats::ModifyAssetAction ParseSpatsModifyTransaction(const CTransaction &tx)
{
assert(tx.IsSpatsModify());
if (tx.vout.size() < 1)
throw CBadTxIn();
const CScript& modification_script = tx.vout.front().scriptPubKey;
if (!modification_script.IsSpatsModify())
throw CBadTxIn();

try{
std::vector<unsigned char> serialized(modification_script.begin() + 1, modification_script.end());
auto action_serialization_size = serialized.size();
const void* const action_serialization_start_address = serialized.data();
CDataStream stream(serialized, SER_NETWORK, PROTOCOL_VERSION );
assert(stream.size() == action_serialization_size);
spats::ModifyAssetAction action(deserialize, stream);
const auto &asset_modification = action.get();
action_serialization_size -= stream.size();
spark::OwnershipProof proof;
stream >> proof;
LogPrintf("ParseSpatsModifyTransaction address ownership proof: %s\n", proof);
const auto& b = get_base(asset_modification);
Address a(spark::Params::get_default());
a.decode(b.initiator_public_address());
const auto scalar_of_proof = spats::Wallet::compute_modify_spark_asset_serialization_scalar(
b, {static_cast<const unsigned char*>(action_serialization_start_address), action_serialization_size});
LogPrintf("ParseSpatsModifyTransaction scalar_of_proof: %s\n", scalar_of_proof);
if (!a.verify_own(scalar_of_proof, proof))
throw CBadTxIn();
return action;
}
catch (...) {
throw CBadTxIn();
}
}

static spats::Action ParseSpatsTransaction(const CTransaction &tx)
{
assert(tx.IsSpatsTransaction());
if (tx.IsSpatsCreate())
return ParseSpatsCreateTransaction(tx);
if (tx.IsSpatsUnregister())
return ParseSpatsUnregisterTransaction(tx);
if (tx.IsSpatsModify())
return ParseSpatsModifyTransaction(tx);
// TODO more
throw CBadTxIn();
}
Expand Down
2 changes: 2 additions & 0 deletions src/spats/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ add_library(spats STATIC
actions.hpp
manager.cpp
manager.hpp
modification.hpp
../utils/constrained_value.hpp
../utils/empty_class.hpp
../utils/enum.hpp
../utils/math.hpp
../utils/scaled_amount.hpp
Expand Down
52 changes: 45 additions & 7 deletions src/spats/actions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "identification.hpp"
#include "base_asset.hpp"
#include "modification.hpp"
#include "spark_asset.hpp"

namespace spats {
Expand Down Expand Up @@ -82,7 +83,9 @@ class CreateAssetAction {
{}

template < typename Stream >
CreateAssetAction( deserialize_type, Stream &is );
CreateAssetAction( deserialize_type, Stream &is )
: asset_( Unserialize( is ) )
{}

template < typename Stream >
void Serialize( Stream &os ) const;
Expand All @@ -98,11 +101,6 @@ class CreateAssetAction {
static SparkAsset Unserialize( Stream &is );
};

template < typename Stream >
CreateAssetAction::CreateAssetAction( deserialize_type, Stream &is )
: asset_( Unserialize( is ) )
{}

template < typename Stream >
void CreateAssetAction::Serialize( Stream &os ) const
{
Expand Down Expand Up @@ -152,7 +150,47 @@ class UnregisterAssetAction {
}
};

using Action = std::variant< CreateAssetAction, UnregisterAssetAction >; // TODO more
class ModifyAssetAction {
public:
explicit ModifyAssetAction( AssetModification asset_modification )
: asset_modification_( std::move( asset_modification ) )
{}

template < typename Stream >
ModifyAssetAction( deserialize_type, Stream &is )
: asset_modification_( Unserialize( is ) )
{}

template < typename Stream >
void Serialize( Stream &os ) const;

const AssetModification &get() const & noexcept { return asset_modification_; }
AssetModification &&get() && noexcept { return std::move( asset_modification_ ); }

private:
AssetModification asset_modification_;
static constexpr std::uint8_t serialization_version = 1;

template < typename Stream >
static AssetModification Unserialize( Stream &is );
};

template < typename Stream >
void ModifyAssetAction::Serialize( Stream &os ) const
{
os << serialization_version;
::Serialize( os, asset_modification_ );
}

template < typename Stream >
AssetModification ModifyAssetAction::Unserialize( Stream &is )
{
std::uint8_t version;
is >> version;
return UnserializeVariant< AssetModification >( is );
}

using Action = std::variant< CreateAssetAction, UnregisterAssetAction, ModifyAssetAction >; // TODO more

using ActionSequence = std::vector< Action >;

Expand Down
Loading

0 comments on commit 8dee0d6

Please sign in to comment.