forked from Qortal/qortal
Merge branch 'asset-unicode' into launch
This commit is contained in:
commit
ed178e744d
@ -8,6 +8,7 @@ import org.qortal.data.transaction.UpdateAssetTransactionData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.utils.Amounts;
|
||||
import org.qortal.utils.Unicode;
|
||||
|
||||
public class Asset {
|
||||
|
||||
@ -23,6 +24,7 @@ public class Asset {
|
||||
|
||||
// Other useful constants
|
||||
|
||||
public static final int MIN_NAME_SIZE = 3;
|
||||
public static final int MAX_NAME_SIZE = 40;
|
||||
public static final int MAX_DESCRIPTION_SIZE = 4000;
|
||||
public static final int MAX_DATA_SIZE = 400000;
|
||||
@ -49,8 +51,8 @@ public class Asset {
|
||||
this.assetData = new AssetData(ownerAddress, issueAssetTransactionData.getAssetName(),
|
||||
issueAssetTransactionData.getDescription(), issueAssetTransactionData.getQuantity(),
|
||||
issueAssetTransactionData.isDivisible(), issueAssetTransactionData.getData(),
|
||||
issueAssetTransactionData.isUnspendable(),
|
||||
issueAssetTransactionData.getTxGroupId(), issueAssetTransactionData.getSignature());
|
||||
issueAssetTransactionData.isUnspendable(), issueAssetTransactionData.getTxGroupId(),
|
||||
issueAssetTransactionData.getSignature(), issueAssetTransactionData.getReducedAssetName());
|
||||
}
|
||||
|
||||
public Asset(Repository repository, long assetId) throws DataException {
|
||||
@ -66,6 +68,10 @@ public class Asset {
|
||||
|
||||
// Processing
|
||||
|
||||
public static String reduceName(String assetName) {
|
||||
return Unicode.sanitize(assetName);
|
||||
}
|
||||
|
||||
public void issue() throws DataException {
|
||||
this.repository.getAssetRepository().save(this.assetData);
|
||||
}
|
||||
|
@ -20,11 +20,17 @@ public class AssetData {
|
||||
private String data;
|
||||
private boolean isUnspendable;
|
||||
private int creationGroupId;
|
||||
|
||||
// No need to expose this via API
|
||||
@XmlTransient
|
||||
@Schema(hidden = true)
|
||||
private byte[] reference;
|
||||
|
||||
// For internal use only
|
||||
@XmlTransient
|
||||
@Schema(hidden = true)
|
||||
private String reducedAssetName;
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAXB serialization
|
||||
@ -32,7 +38,8 @@ public class AssetData {
|
||||
}
|
||||
|
||||
// NOTE: key is Long, not long, because it can be null if asset ID/key not yet assigned.
|
||||
public AssetData(Long assetId, String owner, String name, String description, long quantity, boolean isDivisible, String data, boolean isUnspendable, int creationGroupId, byte[] reference) {
|
||||
public AssetData(Long assetId, String owner, String name, String description, long quantity, boolean isDivisible,
|
||||
String data, boolean isUnspendable, int creationGroupId, byte[] reference, String reducedAssetName) {
|
||||
this.assetId = assetId;
|
||||
this.owner = owner;
|
||||
this.name = name;
|
||||
@ -43,11 +50,13 @@ public class AssetData {
|
||||
this.isUnspendable = isUnspendable;
|
||||
this.creationGroupId = creationGroupId;
|
||||
this.reference = reference;
|
||||
this.reducedAssetName = reducedAssetName;
|
||||
}
|
||||
|
||||
// New asset with unassigned assetId
|
||||
public AssetData(String owner, String name, String description, long quantity, boolean isDivisible, String data, boolean isUnspendable, int creationGroupId, byte[] reference) {
|
||||
this(null, owner, name, description, quantity, isDivisible, data, isUnspendable, creationGroupId, reference);
|
||||
public AssetData(String owner, String name, String description, long quantity, boolean isDivisible, String data,
|
||||
boolean isUnspendable, int creationGroupId, byte[] reference, String reducedAssetName) {
|
||||
this(null, owner, name, description, quantity, isDivisible, data, isUnspendable, creationGroupId, reference, reducedAssetName);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
@ -112,4 +121,12 @@ public class AssetData {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
public String getReducedAssetName() {
|
||||
return this.reducedAssetName;
|
||||
}
|
||||
|
||||
public void setReducedAssetName(String reducedAssetName) {
|
||||
this.reducedAssetName = reducedAssetName;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,10 +3,12 @@ package org.qortal.data.transaction;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||
|
||||
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
|
||||
import org.qortal.account.NullAccount;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.block.GenesisBlock;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
|
||||
@ -48,6 +50,11 @@ public class IssueAssetTransactionData extends TransactionData {
|
||||
@Schema(description = "whether non-owner holders of asset are barred from using asset", example = "false")
|
||||
private boolean isUnspendable;
|
||||
|
||||
// For internal use
|
||||
@Schema(hidden = true)
|
||||
@XmlTransient
|
||||
private String reducedAssetName;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAXB
|
||||
@ -64,12 +71,20 @@ public class IssueAssetTransactionData extends TransactionData {
|
||||
if (parent instanceof GenesisBlock.GenesisInfo && this.issuerPublicKey == null)
|
||||
this.issuerPublicKey = NullAccount.PUBLIC_KEY;
|
||||
|
||||
/*
|
||||
* If we're being constructed as part of the genesis block info inside blockchain config
|
||||
* then we need to construct 'reduced' form of asset name.
|
||||
*/
|
||||
if (parent instanceof GenesisBlock.GenesisInfo && this.reducedAssetName == null)
|
||||
this.reducedAssetName = Asset.reduceName(this.assetName);
|
||||
|
||||
this.creatorPublicKey = this.issuerPublicKey;
|
||||
}
|
||||
|
||||
/** From repository */
|
||||
public IssueAssetTransactionData(BaseTransactionData baseTransactionData,
|
||||
Long assetId, String assetName, String description, long quantity, boolean isDivisible, String data, boolean isUnspendable) {
|
||||
public IssueAssetTransactionData(BaseTransactionData baseTransactionData, Long assetId, String assetName,
|
||||
String description, long quantity, boolean isDivisible, String data, boolean isUnspendable,
|
||||
String reducedAssetName) {
|
||||
super(TransactionType.ISSUE_ASSET, baseTransactionData);
|
||||
|
||||
this.assetId = assetId;
|
||||
@ -80,12 +95,13 @@ public class IssueAssetTransactionData extends TransactionData {
|
||||
this.isDivisible = isDivisible;
|
||||
this.data = data;
|
||||
this.isUnspendable = isUnspendable;
|
||||
this.reducedAssetName = reducedAssetName;
|
||||
}
|
||||
|
||||
/** From network/API */
|
||||
public IssueAssetTransactionData(BaseTransactionData baseTransactionData, String assetName, String description,
|
||||
long quantity, boolean isDivisible, String data, boolean isUnspendable) {
|
||||
this(baseTransactionData, null, assetName, description, quantity, isDivisible, data, isUnspendable);
|
||||
this(baseTransactionData, null, assetName, description, quantity, isDivisible, data, isUnspendable, null);
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
@ -126,4 +142,12 @@ public class IssueAssetTransactionData extends TransactionData {
|
||||
return this.isUnspendable;
|
||||
}
|
||||
|
||||
public String getReducedAssetName() {
|
||||
return this.reducedAssetName;
|
||||
}
|
||||
|
||||
public void setReducedAssetName(String reducedAssetName) {
|
||||
this.reducedAssetName = reducedAssetName;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ public interface AssetRepository {
|
||||
|
||||
public boolean assetExists(String assetName) throws DataException;
|
||||
|
||||
public boolean reducedAssetNameExists(String reducedAssetName) throws DataException;
|
||||
|
||||
public List<AssetData> getAllAssets(Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||
|
||||
public default List<AssetData> getAllAssets() throws DataException {
|
||||
|
@ -25,7 +25,9 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
|
||||
@Override
|
||||
public AssetData fromAssetId(long assetId) throws DataException {
|
||||
String sql = "SELECT owner, asset_name, description, quantity, is_divisible, data, is_unspendable, creation_group_id, reference FROM Assets WHERE asset_id = ?";
|
||||
String sql = "SELECT owner, asset_name, description, quantity, is_divisible, data, "
|
||||
+ "is_unspendable, creation_group_id, reference, reduced_asset_name "
|
||||
+ "FROM Assets WHERE asset_id = ?";
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql, assetId)) {
|
||||
if (resultSet == null)
|
||||
@ -40,9 +42,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
boolean isUnspendable = resultSet.getBoolean(7);
|
||||
int creationGroupId = resultSet.getInt(8);
|
||||
byte[] reference = resultSet.getBytes(9);
|
||||
String reducedAssetName = resultSet.getString(10);
|
||||
|
||||
return new AssetData(assetId, owner, assetName, description, quantity, isDivisible, data, isUnspendable,
|
||||
creationGroupId, reference);
|
||||
creationGroupId, reference, reducedAssetName);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch asset from repository", e);
|
||||
}
|
||||
@ -50,7 +53,9 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
|
||||
@Override
|
||||
public AssetData fromAssetName(String assetName) throws DataException {
|
||||
String sql = "SELECT owner, asset_id, description, quantity, is_divisible, data, is_unspendable, creation_group_id, reference FROM Assets WHERE asset_name = ?";
|
||||
String sql = "SELECT owner, asset_id, description, quantity, is_divisible, data, "
|
||||
+ "is_unspendable, creation_group_id, reference, reduced_asset_name "
|
||||
+ "FROM Assets WHERE asset_name = ?";
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql, assetName)) {
|
||||
if (resultSet == null)
|
||||
@ -65,9 +70,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
boolean isUnspendable = resultSet.getBoolean(7);
|
||||
int creationGroupId = resultSet.getInt(8);
|
||||
byte[] reference = resultSet.getBytes(9);
|
||||
String reducedAssetName = resultSet.getString(10);
|
||||
|
||||
return new AssetData(assetId, owner, assetName, description, quantity, isDivisible, data, isUnspendable,
|
||||
creationGroupId, reference);
|
||||
creationGroupId, reference, reducedAssetName);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch asset from repository", e);
|
||||
}
|
||||
@ -91,10 +97,20 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean reducedAssetNameExists(String reducedAssetName) throws DataException {
|
||||
try {
|
||||
return this.repository.exists("Assets", "reduced_asset_name = ?", reducedAssetName);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to check for asset in repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AssetData> getAllAssets(Integer limit, Integer offset, Boolean reverse) throws DataException {
|
||||
StringBuilder sql = new StringBuilder(256);
|
||||
sql.append("SELECT asset_id, owner, asset_name, description, quantity, is_divisible, data, is_unspendable, creation_group_id, reference "
|
||||
sql.append("SELECT asset_id, owner, asset_name, description, quantity, is_divisible, data, "
|
||||
+ "is_unspendable, creation_group_id, reference, reduced_asset_name "
|
||||
+ "FROM Assets ORDER BY asset_id");
|
||||
if (reverse != null && reverse)
|
||||
sql.append(" DESC");
|
||||
@ -118,9 +134,10 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
boolean isUnspendable = resultSet.getBoolean(8);
|
||||
int creationGroupId = resultSet.getInt(9);
|
||||
byte[] reference = resultSet.getBytes(10);
|
||||
String reducedAssetName = resultSet.getString(11);
|
||||
|
||||
assets.add(new AssetData(assetId, owner, assetName, description, quantity, isDivisible, data,
|
||||
isUnspendable,creationGroupId, reference));
|
||||
isUnspendable,creationGroupId, reference, reducedAssetName));
|
||||
} while (resultSet.next());
|
||||
|
||||
return assets;
|
||||
@ -161,7 +178,8 @@ public class HSQLDBAssetRepository implements AssetRepository {
|
||||
.bind("asset_name", assetData.getName()).bind("description", assetData.getDescription())
|
||||
.bind("quantity", assetData.getQuantity()).bind("is_divisible", assetData.isDivisible())
|
||||
.bind("data", assetData.getData()).bind("is_unspendable", assetData.isUnspendable())
|
||||
.bind("creation_group_id", assetData.getCreationGroupId()).bind("reference", assetData.getReference());
|
||||
.bind("creation_group_id", assetData.getCreationGroupId()).bind("reference", assetData.getReference())
|
||||
.bind("reduced_asset_name", assetData.getReducedAssetName());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
|
@ -355,9 +355,11 @@ public class HSQLDBDatabaseUpdates {
|
||||
+ "quantity BIGINT NOT NULL, is_divisible BOOLEAN NOT NULL, "
|
||||
+ "is_unspendable BOOLEAN NOT NULL DEFAULT FALSE, creation_group_id GroupID NOT NULL DEFAULT 0, "
|
||||
+ "reference Signature NOT NULL, data AssetData NOT NULL DEFAULT '', "
|
||||
+ "PRIMARY KEY (asset_id))");
|
||||
+ "reduced_asset_name AssetName NOT NULL, PRIMARY KEY (asset_id))");
|
||||
// For when a user wants to lookup an asset by name
|
||||
stmt.execute("CREATE INDEX AssetNameIndex on Assets (asset_name)");
|
||||
// For looking up assets by 'reduced' name
|
||||
stmt.execute("CREATE INDEX AssetReducedNameIndex on Assets (reduced_asset_name)");
|
||||
|
||||
// We need a corresponding trigger to make sure new asset_id values are assigned sequentially start from 0
|
||||
stmt.execute("CREATE TRIGGER Asset_ID_Trigger BEFORE INSERT ON Assets "
|
||||
@ -386,7 +388,8 @@ public class HSQLDBDatabaseUpdates {
|
||||
// Issue Asset Transactions
|
||||
stmt.execute("CREATE TABLE IssueAssetTransactions (signature Signature, issuer QortalPublicKey NOT NULL, asset_name AssetName NOT NULL, "
|
||||
+ "description GenericDescription NOT NULL, quantity BIGINT NOT NULL, is_divisible BOOLEAN NOT NULL, asset_id AssetID, "
|
||||
+ "is_unspendable BOOLEAN NOT NULL, data AssetData NOT NULL DEFAULT '', " + TRANSACTION_KEYS + ")");
|
||||
+ "is_unspendable BOOLEAN NOT NULL, data AssetData NOT NULL DEFAULT '', reduced_asset_name AssetName NOT NULL, "
|
||||
+ TRANSACTION_KEYS + ")");
|
||||
|
||||
// Transfer Asset Transactions
|
||||
stmt.execute("CREATE TABLE TransferAssetTransactions (signature Signature, sender QortalPublicKey NOT NULL, recipient QortalAddress NOT NULL, "
|
||||
|
@ -17,7 +17,8 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
|
||||
}
|
||||
|
||||
TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException {
|
||||
String sql = "SELECT asset_name, description, quantity, is_divisible, data, is_unspendable, asset_id FROM IssueAssetTransactions WHERE signature = ?";
|
||||
String sql = "SELECT asset_name, description, quantity, is_divisible, data, is_unspendable, asset_id, reduced_asset_name "
|
||||
+ "FROM IssueAssetTransactions WHERE signature = ?";
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) {
|
||||
if (resultSet == null)
|
||||
@ -35,8 +36,10 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
|
||||
if (assetId == 0 && resultSet.wasNull())
|
||||
assetId = null;
|
||||
|
||||
String reducedAssetName = resultSet.getString(8);
|
||||
|
||||
return new IssueAssetTransactionData(baseTransactionData, assetId, assetName, description, quantity, isDivisible,
|
||||
data, isUnspendable);
|
||||
data, isUnspendable, reducedAssetName);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch issue asset transaction from repository", e);
|
||||
}
|
||||
@ -49,7 +52,7 @@ public class HSQLDBIssueAssetTransactionRepository extends HSQLDBTransactionRepo
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("IssueAssetTransactions");
|
||||
|
||||
saveHelper.bind("signature", issueAssetTransactionData.getSignature()).bind("issuer", issueAssetTransactionData.getIssuerPublicKey())
|
||||
.bind("asset_name", issueAssetTransactionData.getAssetName())
|
||||
.bind("asset_name", issueAssetTransactionData.getAssetName()).bind("reduced_asset_name", issueAssetTransactionData.getReducedAssetName())
|
||||
.bind("description", issueAssetTransactionData.getDescription()).bind("quantity", issueAssetTransactionData.getQuantity())
|
||||
.bind("is_divisible", issueAssetTransactionData.isDivisible()).bind("data", issueAssetTransactionData.getData())
|
||||
.bind("is_unspendable", issueAssetTransactionData.isUnspendable()).bind("asset_id", issueAssetTransactionData.getAssetId());
|
||||
|
@ -7,9 +7,11 @@ import org.qortal.account.Account;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.data.transaction.IssueAssetTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.naming.Name;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.utils.Amounts;
|
||||
import org.qortal.utils.Unicode;
|
||||
|
||||
import com.google.common.base.Utf8;
|
||||
|
||||
@ -40,15 +42,29 @@ public class IssueAssetTransaction extends Transaction {
|
||||
return this.getCreator();
|
||||
}
|
||||
|
||||
private String getReducedAssetName() {
|
||||
if (this.issueAssetTransactionData.getReducedAssetName() == null) {
|
||||
String reducedAssetName = Name.reduceName(this.issueAssetTransactionData.getAssetName());
|
||||
this.issueAssetTransactionData.setReducedAssetName(reducedAssetName);
|
||||
}
|
||||
|
||||
return this.issueAssetTransactionData.getReducedAssetName();
|
||||
}
|
||||
|
||||
// Processing
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Check name size bounds
|
||||
int assetNameLength = Utf8.encodedLength(this.issueAssetTransactionData.getAssetName());
|
||||
if (assetNameLength < 1 || assetNameLength > Asset.MAX_NAME_SIZE)
|
||||
String assetName = this.issueAssetTransactionData.getAssetName();
|
||||
int assetNameLength = Utf8.encodedLength(assetName);
|
||||
if (assetNameLength < Asset.MIN_NAME_SIZE || assetNameLength > Asset.MAX_NAME_SIZE)
|
||||
return ValidationResult.INVALID_NAME_LENGTH;
|
||||
|
||||
// Check name is in normalized form (no leading/trailing whitespace, etc.)
|
||||
if (!assetName.equals(Unicode.normalize(assetName)))
|
||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||
|
||||
// Check description size bounds
|
||||
int assetDescriptionlength = Utf8.encodedLength(this.issueAssetTransactionData.getDescription());
|
||||
if (assetDescriptionlength < 1 || assetDescriptionlength > Asset.MAX_DESCRIPTION_SIZE)
|
||||
@ -74,13 +90,16 @@ public class IssueAssetTransaction extends Transaction {
|
||||
if (issuer.getConfirmedBalance(Asset.QORT) < this.issueAssetTransactionData.getFee())
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
// Fill in missing reduced name. Caller is likely to save this as next step.
|
||||
getReducedAssetName();
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult isProcessable() throws DataException {
|
||||
// Check the asset name isn't already taken.
|
||||
if (this.repository.getAssetRepository().assetExists(this.issueAssetTransactionData.getAssetName()))
|
||||
// Check the name isn't already taken
|
||||
if (this.repository.getAssetRepository().reducedAssetNameExists(getReducedAssetName()))
|
||||
return ValidationResult.ASSET_ALREADY_EXISTS;
|
||||
|
||||
return ValidationResult.OK;
|
||||
|
@ -1,12 +1,21 @@
|
||||
package org.qortal.test.assets;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.data.transaction.IssueAssetTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TestAccount;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.test.common.transaction.TestTransaction;
|
||||
import org.qortal.transaction.Transaction;
|
||||
import org.qortal.transaction.Transaction.ValidationResult;
|
||||
import org.qortal.utils.Amounts;
|
||||
|
||||
public class MiscTests extends Common {
|
||||
@ -21,6 +30,32 @@ public class MiscTests extends Common {
|
||||
Common.orphanCheck();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAssetWithExistingName() throws DataException {
|
||||
try (Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
|
||||
String assetName = "test-asset";
|
||||
String description = "description";
|
||||
long quantity = 12345678L;
|
||||
boolean isDivisible = true;
|
||||
String data = "{}";
|
||||
boolean isUnspendable = false;
|
||||
|
||||
TransactionData transactionData = new IssueAssetTransactionData(TestTransaction.generateBase(alice), assetName, description, quantity, isDivisible, data, isUnspendable);
|
||||
TransactionUtils.signAndMint(repository, transactionData, alice);
|
||||
|
||||
String duplicateAssetName = "TEST-Ásset";
|
||||
transactionData = new IssueAssetTransactionData(TestTransaction.generateBase(alice), duplicateAssetName, description, quantity, isDivisible, data, isUnspendable);
|
||||
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
transaction.sign(alice);
|
||||
|
||||
ValidationResult result = transaction.importAsUnconfirmed();
|
||||
assertTrue("Transaction should be invalid", ValidationResult.OK != result);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalcCommitmentWithRoundUp() throws DataException {
|
||||
long amount = 1234_87654321L;
|
||||
|
Loading…
Reference in New Issue
Block a user