update UTXO schema

This commit is contained in:
julian 2023-03-06 14:39:41 -06:00
parent d18ea7f2bf
commit 251f90834c
3 changed files with 266 additions and 48 deletions

View file

@ -18,6 +18,7 @@ class UTXO {
required this.blockHash, required this.blockHash,
required this.blockHeight, required this.blockHeight,
required this.blockTime, required this.blockTime,
this.address,
this.otherData, this.otherData,
}); });
@ -48,6 +49,8 @@ class UTXO {
late final int? blockTime; late final int? blockTime;
late final String? address;
late final String? otherData; late final String? otherData;
int getConfirmations(int currentChainHeight) { int getConfirmations(int currentChainHeight) {
@ -75,5 +78,7 @@ class UTXO {
"blockHash: $blockHash, " "blockHash: $blockHash, "
"blockHeight: $blockHeight, " "blockHeight: $blockHeight, "
"blockTime: $blockTime, " "blockTime: $blockTime, "
"address: $address, "
"otherData: $otherData, "
"}"; "}";
} }

View file

@ -17,63 +17,68 @@ const UTXOSchema = CollectionSchema(
name: r'UTXO', name: r'UTXO',
id: 5934032492047519621, id: 5934032492047519621,
properties: { properties: {
r'blockHash': PropertySchema( r'address': PropertySchema(
id: 0, id: 0,
name: r'address',
type: IsarType.string,
),
r'blockHash': PropertySchema(
id: 1,
name: r'blockHash', name: r'blockHash',
type: IsarType.string, type: IsarType.string,
), ),
r'blockHeight': PropertySchema( r'blockHeight': PropertySchema(
id: 1, id: 2,
name: r'blockHeight', name: r'blockHeight',
type: IsarType.long, type: IsarType.long,
), ),
r'blockTime': PropertySchema( r'blockTime': PropertySchema(
id: 2, id: 3,
name: r'blockTime', name: r'blockTime',
type: IsarType.long, type: IsarType.long,
), ),
r'blockedReason': PropertySchema( r'blockedReason': PropertySchema(
id: 3, id: 4,
name: r'blockedReason', name: r'blockedReason',
type: IsarType.string, type: IsarType.string,
), ),
r'isBlocked': PropertySchema( r'isBlocked': PropertySchema(
id: 4, id: 5,
name: r'isBlocked', name: r'isBlocked',
type: IsarType.bool, type: IsarType.bool,
), ),
r'isCoinbase': PropertySchema( r'isCoinbase': PropertySchema(
id: 5, id: 6,
name: r'isCoinbase', name: r'isCoinbase',
type: IsarType.bool, type: IsarType.bool,
), ),
r'name': PropertySchema( r'name': PropertySchema(
id: 6, id: 7,
name: r'name', name: r'name',
type: IsarType.string, type: IsarType.string,
), ),
r'otherData': PropertySchema( r'otherData': PropertySchema(
id: 7, id: 8,
name: r'otherData', name: r'otherData',
type: IsarType.string, type: IsarType.string,
), ),
r'txid': PropertySchema( r'txid': PropertySchema(
id: 8, id: 9,
name: r'txid', name: r'txid',
type: IsarType.string, type: IsarType.string,
), ),
r'value': PropertySchema( r'value': PropertySchema(
id: 9, id: 10,
name: r'value', name: r'value',
type: IsarType.long, type: IsarType.long,
), ),
r'vout': PropertySchema( r'vout': PropertySchema(
id: 10, id: 11,
name: r'vout', name: r'vout',
type: IsarType.long, type: IsarType.long,
), ),
r'walletId': PropertySchema( r'walletId': PropertySchema(
id: 11, id: 12,
name: r'walletId', name: r'walletId',
type: IsarType.string, type: IsarType.string,
) )
@ -143,6 +148,12 @@ int _uTXOEstimateSize(
Map<Type, List<int>> allOffsets, Map<Type, List<int>> allOffsets,
) { ) {
var bytesCount = offsets.last; var bytesCount = offsets.last;
{
final value = object.address;
if (value != null) {
bytesCount += 3 + value.length * 3;
}
}
{ {
final value = object.blockHash; final value = object.blockHash;
if (value != null) { if (value != null) {
@ -173,18 +184,19 @@ void _uTXOSerialize(
List<int> offsets, List<int> offsets,
Map<Type, List<int>> allOffsets, Map<Type, List<int>> allOffsets,
) { ) {
writer.writeString(offsets[0], object.blockHash); writer.writeString(offsets[0], object.address);
writer.writeLong(offsets[1], object.blockHeight); writer.writeString(offsets[1], object.blockHash);
writer.writeLong(offsets[2], object.blockTime); writer.writeLong(offsets[2], object.blockHeight);
writer.writeString(offsets[3], object.blockedReason); writer.writeLong(offsets[3], object.blockTime);
writer.writeBool(offsets[4], object.isBlocked); writer.writeString(offsets[4], object.blockedReason);
writer.writeBool(offsets[5], object.isCoinbase); writer.writeBool(offsets[5], object.isBlocked);
writer.writeString(offsets[6], object.name); writer.writeBool(offsets[6], object.isCoinbase);
writer.writeString(offsets[7], object.otherData); writer.writeString(offsets[7], object.name);
writer.writeString(offsets[8], object.txid); writer.writeString(offsets[8], object.otherData);
writer.writeLong(offsets[9], object.value); writer.writeString(offsets[9], object.txid);
writer.writeLong(offsets[10], object.vout); writer.writeLong(offsets[10], object.value);
writer.writeString(offsets[11], object.walletId); writer.writeLong(offsets[11], object.vout);
writer.writeString(offsets[12], object.walletId);
} }
UTXO _uTXODeserialize( UTXO _uTXODeserialize(
@ -194,18 +206,19 @@ UTXO _uTXODeserialize(
Map<Type, List<int>> allOffsets, Map<Type, List<int>> allOffsets,
) { ) {
final object = UTXO( final object = UTXO(
blockHash: reader.readStringOrNull(offsets[0]), address: reader.readStringOrNull(offsets[0]),
blockHeight: reader.readLongOrNull(offsets[1]), blockHash: reader.readStringOrNull(offsets[1]),
blockTime: reader.readLongOrNull(offsets[2]), blockHeight: reader.readLongOrNull(offsets[2]),
blockedReason: reader.readStringOrNull(offsets[3]), blockTime: reader.readLongOrNull(offsets[3]),
isBlocked: reader.readBool(offsets[4]), blockedReason: reader.readStringOrNull(offsets[4]),
isCoinbase: reader.readBool(offsets[5]), isBlocked: reader.readBool(offsets[5]),
name: reader.readString(offsets[6]), isCoinbase: reader.readBool(offsets[6]),
otherData: reader.readStringOrNull(offsets[7]), name: reader.readString(offsets[7]),
txid: reader.readString(offsets[8]), otherData: reader.readStringOrNull(offsets[8]),
value: reader.readLong(offsets[9]), txid: reader.readString(offsets[9]),
vout: reader.readLong(offsets[10]), value: reader.readLong(offsets[10]),
walletId: reader.readString(offsets[11]), vout: reader.readLong(offsets[11]),
walletId: reader.readString(offsets[12]),
); );
object.id = id; object.id = id;
return object; return object;
@ -221,26 +234,28 @@ P _uTXODeserializeProp<P>(
case 0: case 0:
return (reader.readStringOrNull(offset)) as P; return (reader.readStringOrNull(offset)) as P;
case 1: case 1:
return (reader.readLongOrNull(offset)) as P; return (reader.readStringOrNull(offset)) as P;
case 2: case 2:
return (reader.readLongOrNull(offset)) as P; return (reader.readLongOrNull(offset)) as P;
case 3: case 3:
return (reader.readStringOrNull(offset)) as P; return (reader.readLongOrNull(offset)) as P;
case 4: case 4:
return (reader.readBool(offset)) as P; return (reader.readStringOrNull(offset)) as P;
case 5: case 5:
return (reader.readBool(offset)) as P; return (reader.readBool(offset)) as P;
case 6: case 6:
return (reader.readString(offset)) as P; return (reader.readBool(offset)) as P;
case 7: case 7:
return (reader.readStringOrNull(offset)) as P;
case 8:
return (reader.readString(offset)) as P; return (reader.readString(offset)) as P;
case 8:
return (reader.readStringOrNull(offset)) as P;
case 9: case 9:
return (reader.readLong(offset)) as P; return (reader.readString(offset)) as P;
case 10: case 10:
return (reader.readLong(offset)) as P; return (reader.readLong(offset)) as P;
case 11: case 11:
return (reader.readLong(offset)) as P;
case 12:
return (reader.readString(offset)) as P; return (reader.readString(offset)) as P;
default: default:
throw IsarError('Unknown property with id $propertyId'); throw IsarError('Unknown property with id $propertyId');
@ -608,6 +623,150 @@ extension UTXOQueryWhere on QueryBuilder<UTXO, UTXO, QWhereClause> {
} }
extension UTXOQueryFilter on QueryBuilder<UTXO, UTXO, QFilterCondition> { extension UTXOQueryFilter on QueryBuilder<UTXO, UTXO, QFilterCondition> {
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressIsNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull(
property: r'address',
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressIsNotNull() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNotNull(
property: r'address',
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressEqualTo(
String? value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressGreaterThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
include: include,
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressLessThan(
String? value, {
bool include = false,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.lessThan(
include: include,
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressBetween(
String? lower,
String? upper, {
bool includeLower = true,
bool includeUpper = true,
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.between(
property: r'address',
lower: lower,
includeLower: includeLower,
upper: upper,
includeUpper: includeUpper,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressStartsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.startsWith(
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressEndsWith(
String value, {
bool caseSensitive = true,
}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.endsWith(
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressContains(String value,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.contains(
property: r'address',
value: value,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressMatches(String pattern,
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.matches(
property: r'address',
wildcard: pattern,
caseSensitive: caseSensitive,
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressIsEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.equalTo(
property: r'address',
value: '',
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> addressIsNotEmpty() {
return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(FilterCondition.greaterThan(
property: r'address',
value: '',
));
});
}
QueryBuilder<UTXO, UTXO, QAfterFilterCondition> blockHashIsNull() { QueryBuilder<UTXO, UTXO, QAfterFilterCondition> blockHashIsNull() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addFilterCondition(const FilterCondition.isNull( return query.addFilterCondition(const FilterCondition.isNull(
@ -1749,6 +1908,18 @@ extension UTXOQueryObject on QueryBuilder<UTXO, UTXO, QFilterCondition> {}
extension UTXOQueryLinks on QueryBuilder<UTXO, UTXO, QFilterCondition> {} extension UTXOQueryLinks on QueryBuilder<UTXO, UTXO, QFilterCondition> {}
extension UTXOQuerySortBy on QueryBuilder<UTXO, UTXO, QSortBy> { extension UTXOQuerySortBy on QueryBuilder<UTXO, UTXO, QSortBy> {
QueryBuilder<UTXO, UTXO, QAfterSortBy> sortByAddress() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'address', Sort.asc);
});
}
QueryBuilder<UTXO, UTXO, QAfterSortBy> sortByAddressDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'address', Sort.desc);
});
}
QueryBuilder<UTXO, UTXO, QAfterSortBy> sortByBlockHash() { QueryBuilder<UTXO, UTXO, QAfterSortBy> sortByBlockHash() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'blockHash', Sort.asc); return query.addSortBy(r'blockHash', Sort.asc);
@ -1895,6 +2066,18 @@ extension UTXOQuerySortBy on QueryBuilder<UTXO, UTXO, QSortBy> {
} }
extension UTXOQuerySortThenBy on QueryBuilder<UTXO, UTXO, QSortThenBy> { extension UTXOQuerySortThenBy on QueryBuilder<UTXO, UTXO, QSortThenBy> {
QueryBuilder<UTXO, UTXO, QAfterSortBy> thenByAddress() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'address', Sort.asc);
});
}
QueryBuilder<UTXO, UTXO, QAfterSortBy> thenByAddressDesc() {
return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'address', Sort.desc);
});
}
QueryBuilder<UTXO, UTXO, QAfterSortBy> thenByBlockHash() { QueryBuilder<UTXO, UTXO, QAfterSortBy> thenByBlockHash() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addSortBy(r'blockHash', Sort.asc); return query.addSortBy(r'blockHash', Sort.asc);
@ -2053,6 +2236,13 @@ extension UTXOQuerySortThenBy on QueryBuilder<UTXO, UTXO, QSortThenBy> {
} }
extension UTXOQueryWhereDistinct on QueryBuilder<UTXO, UTXO, QDistinct> { extension UTXOQueryWhereDistinct on QueryBuilder<UTXO, UTXO, QDistinct> {
QueryBuilder<UTXO, UTXO, QDistinct> distinctByAddress(
{bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) {
return query.addDistinctBy(r'address', caseSensitive: caseSensitive);
});
}
QueryBuilder<UTXO, UTXO, QDistinct> distinctByBlockHash( QueryBuilder<UTXO, UTXO, QDistinct> distinctByBlockHash(
{bool caseSensitive = true}) { {bool caseSensitive = true}) {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
@ -2140,6 +2330,12 @@ extension UTXOQueryProperty on QueryBuilder<UTXO, UTXO, QQueryProperty> {
}); });
} }
QueryBuilder<UTXO, String?, QQueryOperations> addressProperty() {
return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'address');
});
}
QueryBuilder<UTXO, String?, QQueryOperations> blockHashProperty() { QueryBuilder<UTXO, String?, QQueryOperations> blockHashProperty() {
return QueryBuilder.apply(this, (query) { return QueryBuilder.apply(this, (query) {
return query.addPropertyName(r'blockHash'); return query.addPropertyName(r'blockHash');

View file

@ -1806,8 +1806,10 @@ class BitcoinWallet extends CoinServiceAPI
for (int i = 0; i < fetchedUtxoList.length; i++) { for (int i = 0; i < fetchedUtxoList.length; i++) {
for (int j = 0; j < fetchedUtxoList[i].length; j++) { for (int j = 0; j < fetchedUtxoList[i].length; j++) {
final jsonUTXO = fetchedUtxoList[i][j];
final txn = await cachedElectrumXClient.getTransaction( final txn = await cachedElectrumXClient.getTransaction(
txHash: fetchedUtxoList[i][j]["tx_hash"] as String, txHash: jsonUTXO["tx_hash"] as String,
verbose: true, verbose: true,
coin: coin, coin: coin,
); );
@ -1815,7 +1817,7 @@ class BitcoinWallet extends CoinServiceAPI
// fetch stored tx to see if paynym notification tx and block utxo // fetch stored tx to see if paynym notification tx and block utxo
final storedTx = await db.getTransaction( final storedTx = await db.getTransaction(
walletId, walletId,
fetchedUtxoList[i][j]["tx_hash"] as String, jsonUTXO["tx_hash"] as String,
); );
bool shouldBlock = false; bool shouldBlock = false;
@ -1832,18 +1834,33 @@ class BitcoinWallet extends CoinServiceAPI
blockReason = "Incoming paynym notification transaction."; blockReason = "Incoming paynym notification transaction.";
} }
final vout = jsonUTXO["tx_pos"] as int;
final outputs = txn["vout"] as List;
String? utxoOwnerAddress;
// get UTXO owner address
for (final output in outputs) {
if (output["n"] == vout) {
utxoOwnerAddress =
output["scriptPubKey"]?["addresses"]?[0] as String? ??
output["scriptPubKey"]?["address"] as String?;
}
}
final utxo = isar_models.UTXO( final utxo = isar_models.UTXO(
walletId: walletId, walletId: walletId,
txid: txn["txid"] as String, txid: txn["txid"] as String,
vout: fetchedUtxoList[i][j]["tx_pos"] as int, vout: vout,
value: fetchedUtxoList[i][j]["value"] as int, value: jsonUTXO["value"] as int,
name: "", name: "",
isBlocked: shouldBlock, isBlocked: shouldBlock,
blockedReason: blockReason, blockedReason: blockReason,
isCoinbase: txn["is_coinbase"] as bool? ?? false, isCoinbase: txn["is_coinbase"] as bool? ?? false,
blockHash: txn["blockhash"] as String?, blockHash: txn["blockhash"] as String?,
blockHeight: fetchedUtxoList[i][j]["height"] as int?, blockHeight: jsonUTXO["height"] as int?,
blockTime: txn["blocktime"] as int?, blockTime: txn["blocktime"] as int?,
address: utxoOwnerAddress,
); );
satoshiBalanceTotal += utxo.value; satoshiBalanceTotal += utxo.value;