Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 6 additions & 1 deletion lib/data_structures.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export 'src/data_structures/utxo_pool.dart'
show UtxoPool, UtxoSelectionStrategy;
export 'src/data_structures/transaction_factory.dart'
show FeeType, TransactionInfo, createDRTransaction, createVTTransaction;
show
FeeType,
TransactionInfo,
createDRTransaction,
createVTTransaction,
createMetadataOutput;
export 'src/data_structures/utxo.dart';
4 changes: 3 additions & 1 deletion lib/src/crypto/address.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class Address {
FeeType? feeType,
int fee = 0,
required dynamic networkSource,
String? metadata,
}) async {
return await createVTTransaction(
outputs: outputs,
Expand All @@ -101,7 +102,8 @@ class Address {
feeType: feeType,
fee: fee,
utxoStrategy: utxoStrategy,
networkSource: networkSource);
networkSource: networkSource,
metadata: metadata);
}

Future<DRTransaction> createDRT({
Expand Down
42 changes: 37 additions & 5 deletions lib/src/data_structures/transaction_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import 'package:witnet/src/crypto/address.dart' show Address;
import 'package:witnet/src/crypto/secp256k1/private_key.dart';
import 'package:witnet/constants.dart'
show ALPHA, GAMMA, INPUT_SIZE, OUTPUT_SIZE;
import 'package:witnet/src/schema/schema.dart' show PublicKeyHash;
import 'package:witnet/src/utils/transformations/transformations.dart'
show isHexStringOfLength;
import '../../data_structures.dart';
import 'package:convert/convert.dart';

class TransactionInfo {
final List<Input> inputs;
Expand All @@ -34,6 +38,26 @@ enum FeeType {
Weighted,
}

ValueTransferOutput createMetadataOutput(String metadata) {
if (!isHexStringOfLength(metadata, 20)) {
throw TransactionError(-1, 'Metadata must be a 20-byte hex string');
}

final cleanMetadata =
metadata.startsWith('0x') ? metadata.substring(2) : metadata;

// Convert hex string into raw List of bytes
final metadataBytes = hex.decode(cleanMetadata);

final metadataPkh = PublicKeyHash()..hash = metadataBytes;

return ValueTransferOutput.fromJson({
'pkh': metadataPkh.address,
'value': 1,
'time_lock': 0,
});
}

Future<dynamic> createVTTransaction({
required List<ValueTransferOutput> outputs,
required WitPrivateKey privateKey,
Expand All @@ -42,11 +66,18 @@ Future<dynamic> createVTTransaction({
required UtxoSelectionStrategy utxoStrategy,
FeeType? feeType,
int fee = 0,
String? metadata,
}) async {
List<ValueTransferOutput> allOutputs = outputs;

if (metadata != null) {
allOutputs.add(createMetadataOutput(metadata));
}

int outputValue = 0;
int totalUtxoValue = 0;
int selectedUtxoValue = 0;
outputs.forEach((ValueTransferOutput output) {
allOutputs.forEach((ValueTransferOutput output) {
outputValue += output.value.toInt();
});

Expand Down Expand Up @@ -92,7 +123,7 @@ Future<dynamic> createVTTransaction({
switch (feeType ?? absFee) {
case FeeType.Absolute:
if (change > fee) {
outputs.add(_changeAddress.receive(change - fee));
allOutputs.add(_changeAddress.receive(change - fee));
} else if (change == fee) {
// do nothing with the change since it is the fee.
} else {
Expand All @@ -110,7 +141,7 @@ Future<dynamic> createVTTransaction({
case FeeType.Weighted:
print('Current Change: $change');
var inputCount = inputs.length;
var outputCount = outputs.length;
var outputCount = allOutputs.length;
var currentWeight = vttWeight(inputCount, outputCount);
print('Inputs: $inputCount, Outputs: $outputCount');
print('Current Weight -> $currentWeight');
Expand All @@ -131,15 +162,16 @@ Future<dynamic> createVTTransaction({
newWeight = vttWeight(inputs.length, outputCount + 1);
}
}
outputs.add(_changeAddress.receive(change - newWeight));
allOutputs.add(_changeAddress.receive(change - newWeight));
} else {
// need additional utxos to cover the weighted fee
}
break;
}
}

VTTransactionBody body = VTTransactionBody(inputs: inputs, outputs: outputs);
VTTransactionBody body =
VTTransactionBody(inputs: inputs, outputs: allOutputs);
VTTransaction transaction = VTTransaction(body: body, signatures: []);

KeyedSignature signature =
Expand Down
14 changes: 14 additions & 0 deletions lib/src/schema/extensions/value_transfer_transaction.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:witnet/schema.dart';

extension Metadata on VTTransaction {
List<PublicKeyHash> get metadata {
List<PublicKeyHash> metadata = this
.body
.outputs
.where((ValueTransferOutput output) => output.value == 1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the metadata only supposed to be a single output in the 0 index?
what happens when a user pays with a 1 nWIT fee?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing happens, if someone tries to use that metadata it won't work for their use case

witnet/witnet-rust@17d69af#diff-0c53a100000d5c8106122c6a1a57e0c124444d4605df8e5eba00a43cd7594a32R1628

.map((ValueTransferOutput output) => output.pkh)
.toList();

return metadata;
}
}
16 changes: 16 additions & 0 deletions lib/src/utils/transformations/transformations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,19 @@ Uint8List toUtf16Bytes(String string,
}

bool isStringNullOrEmpty(String? string) => string == null || string.isEmpty;

bool isHexStringOfLength(String str, int length) {
return isHexString(str) &&
((str.startsWith("0x") && (str.substring(2).length == length * 2)) ||
(str.length == length * 2));
}

bool isHexString(String str) {
final hexRegex = RegExp(r'^[a-fA-F0-9]+$');

if (str.startsWith("0x")) {
return hexRegex.hasMatch(str.substring(2));
} else {
return hexRegex.hasMatch(str);
}
}
20 changes: 20 additions & 0 deletions test/data_structures/create_metadata_output_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:test/expect.dart';
import 'package:test/scaffolding.dart';
import 'package:witnet/schema.dart';
import 'package:witnet/src/data_structures/transaction_factory.dart';

void main() async {
group("create_metadata_output", () {
test("Create Metadata Output", () {
ValueTransferOutput vto1 =
createMetadataOutput("0x1111AbA2164AcdC6D291b08DfB374280035E1111");
expect(vto1.value.toInt(), 1);
expect(vto1.pkh.address, 'wit1zyg6hgskftxud553kzxlkd6zsqp4uyg3zd9q6h');

ValueTransferOutput vto2 =
createMetadataOutput("0x77703aE126B971c9946d562F41Dd47071dA00777");
expect(vto2.value.toInt(), 1);
expect(vto2.pkh.address, 'wit1wacr4cfxh9cun9rd2ch5rh28quw6qpmhk0ydga');
});
});
}