Skip to content
4 changes: 2 additions & 2 deletions lib/flutter_libsparkmobile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const kSparkBaseDerivationPath = "m/44'/136'/0'/$kSparkChain/";
const kSparkBaseDerivationPathTestnet = "m/44'/1'/0'/$kSparkChain/";

const kMaxNameLength =
20; // max 20 symbols, alphanumerical or "-_.", case-insensitive
const kNameRegexString = r'^[a-zA-Z0-9\-_\.]+$';
20; // max 20 symbols, alphanumerical or "-.", case-insensitive
const kNameRegexString = r'^[a-zA-Z0-9.-]+$';
const kMaxAdditionalInfoLengthBytes = 1024;
const kMaxNameRegistrationLengthYears = 10;
const kStage3CommunityFundAddressMainNet = "aFA2TbqG9cnhhzX5Yny2pBJRK5EaEqLCH7";
Expand Down
12 changes: 11 additions & 1 deletion src/deps/sparkmobile/src/sparkname.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace spark {
struct CSparkNameTxData
{
public:
static const uint16_t CURRENT_VERSION = 1;
static const uint16_t CURRENT_VERSION = 2;

public:
uint16_t nVersion{CURRENT_VERSION}; // version
Expand All @@ -27,20 +27,30 @@ struct CSparkNameTxData
std::string additionalInfo;
// failsafe if the hash of the transaction data is can't be converted to a scalar for proof creation/verification
uint32_t hashFailsafe{0};
// Registration-only; add transfer fields if mobile starts creating Spark Name transfers.
uint8_t operationType{0};

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
void SerializationOp(Stream &s, Operation ser_action)
{
READWRITE(nVersion);
if (nVersion != 1 && nVersion != 2)
throw std::ios_base::failure("Unsupported Spark Name transaction data version");
READWRITE(inputsHash);
READWRITE(name);
READWRITE(sparkAddress);
READWRITE(addressOwnershipProof);
READWRITE(sparkNameValidityBlocks);
READWRITE(additionalInfo);
READWRITE(hashFailsafe);
if (nVersion == 2)
{
READWRITE(operationType);
if (operationType != 0)
throw std::ios_base::failure("Unsupported Spark Name operation type");
}
}
};

Expand Down
28 changes: 28 additions & 0 deletions src/deps/sparkmobile/tests/spark_name_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ BOOST_AUTO_TEST_CASE(spark_names)
BOOST_CHECK_EQUAL(decodedData.sparkAddress, sparkNameData.sparkAddress);
BOOST_CHECK_EQUAL(decodedData.sparkNameValidityBlocks, sparkNameData.sparkNameValidityBlocks);
BOOST_CHECK_EQUAL(decodedData.additionalInfo, sparkNameData.additionalInfo);
BOOST_CHECK_EQUAL(decodedData.nVersion, uint16_t{2});
BOOST_CHECK_EQUAL((int)decodedData.operationType, 0);
BOOST_CHECK(!decodedData.addressOwnershipProof.empty());

spark::OwnershipProof deserializedOwnershipProof;
Expand All @@ -54,4 +56,30 @@ BOOST_AUTO_TEST_CASE(spark_names)
BOOST_CHECK(address.verify_own(m, deserializedOwnershipProof));
}

BOOST_AUTO_TEST_CASE(rejects_unsupported_spark_name_data)
{
spark::CSparkNameTxData data;
data.nVersion = 3;
CDataStream serialized(SER_NETWORK, PROTOCOL_VERSION);
BOOST_CHECK_THROW(serialized << data, std::ios_base::failure);

data.nVersion = 2;
CDataStream encodedData(SER_NETWORK, PROTOCOL_VERSION);
encodedData << data;
encodedData[0] = 3;
encodedData[1] = 0;
BOOST_CHECK_THROW(encodedData >> data, std::ios_base::failure);

data.nVersion = 2;
data.operationType = 1;
CDataStream unsupportedOperation(SER_NETWORK, PROTOCOL_VERSION);
BOOST_CHECK_THROW(unsupportedOperation << data, std::ios_base::failure);

data.operationType = 0;
CDataStream encodedOperation(SER_NETWORK, PROTOCOL_VERSION);
encodedOperation << data;
encodedOperation[encodedOperation.size() - 1] = 1;
BOOST_CHECK_THROW(encodedOperation >> data, std::ios_base::failure);
}

BOOST_AUTO_TEST_SUITE_END()
6 changes: 6 additions & 0 deletions test/flutter_libsparkmobile_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import 'package:flutter_libsparkmobile/src/extensions.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
test('Spark Names reject underscores', () {
final pattern = RegExp(kNameRegexString);
expect(pattern.hasMatch('NAME-FOR.TESTING'), isTrue);
expect(pattern.hasMatch('NAME_FOR_TESTING'), isFalse);
});

test('mnemonic to address test', () async {
// Generate key data from the mnemonic.
//
Expand Down