Changeset View
Changeset View
Standalone View
Standalone View
web/cashtab/src/utils/cashMethods.js
Show First 20 Lines • Show All 481 Lines • ▼ Show 20 Lines | if ( | ||||
!wallet.Path145.publicKey || | !wallet.Path145.publicKey || | ||||
!wallet.Path245.publicKey | !wallet.Path245.publicKey | ||||
) { | ) { | ||||
return true; | return true; | ||||
} | } | ||||
return false; | return false; | ||||
}; | }; | ||||
export const isExcludedUtxo = (utxo, utxoArray) => { | |||||
/* | |||||
utxo is a single utxo of model | |||||
{ | |||||
height: 724992 | |||||
tx_hash: "8d4bdedb7c4443412e0c2f316a330863aef54d9ba73560ca60cca6408527b247" | |||||
tx_pos: 0 | |||||
value: 10200 | |||||
} | |||||
utxoArray is an array of utxos | |||||
*/ | |||||
let isExcludedUtxo = true; | |||||
const { tx_hash, tx_pos, value } = utxo; | |||||
for (let i = 0; i < utxoArray.length; i += 1) { | |||||
const thisUtxo = utxoArray[i]; | |||||
// NOTE | |||||
// You can't match height, as this changes from 0 to blockheight after confirmation | |||||
//const thisUtxoHeight = thisUtxo.height; | |||||
const thisUtxoTxid = thisUtxo.tx_hash; | |||||
const thisUtxoTxPos = thisUtxo.tx_pos; | |||||
const thisUtxoValue = thisUtxo.value; | |||||
// If you find a utxo such that each object key is identical | |||||
if ( | |||||
tx_hash === thisUtxoTxid && | |||||
tx_pos === thisUtxoTxPos && | |||||
value === thisUtxoValue | |||||
) { | |||||
// Then this utxo is not excluded from the array | |||||
isExcludedUtxo = false; | |||||
} | |||||
} | |||||
return isExcludedUtxo; | |||||
}; | |||||
export const whichUtxosWereAdded = (previousUtxos, currentUtxos) => { | |||||
let utxosAddedFlag = false; | |||||
const utxosAdded = []; | |||||
// Iterate over currentUtxos | |||||
// For each currentUtxo -- does it exist in previousUtxos? | |||||
// If no, it's added | |||||
// Note that the inputs are arrays of arrays, model | |||||
/* | |||||
previousUtxos = [{address: 'string', utxos: []}, ...] | |||||
*/ | |||||
// Iterate over the currentUtxos array of {address: 'string', utxos: []} objects | |||||
for (let i = 0; i < currentUtxos.length; i += 1) { | |||||
// Take the first object | |||||
const thisCurrentUtxoObject = currentUtxos[i]; | |||||
const thisCurrentUtxoObjectAddress = thisCurrentUtxoObject.address; | |||||
const thisCurrentUtxoObjectUtxos = thisCurrentUtxoObject.utxos; | |||||
// Iterate over the previousUtxos array of {address: 'string', utxos: []} objects | |||||
for (let j = 0; j < previousUtxos.length; j += 1) { | |||||
const thisPreviousUtxoObject = previousUtxos[j]; | |||||
const thisPreviousUtxoObjectAddress = | |||||
thisPreviousUtxoObject.address; | |||||
// When you find the utxos object at the same address | |||||
if ( | |||||
thisCurrentUtxoObjectAddress === thisPreviousUtxoObjectAddress | |||||
) { | |||||
// Create a utxosAddedObject with the address | |||||
const utxosAddedObject = { | |||||
address: thisCurrentUtxoObjectAddress, | |||||
utxos: [], | |||||
}; | |||||
utxosAdded.push(utxosAddedObject); | |||||
// Grab the previousUtxoObject utxos array. thisCurrentUtxoObjectUtxos has changed compared to thisPreviousUtxoObjectUtxos | |||||
const thisPreviousUtxoObjectUtxos = | |||||
thisPreviousUtxoObject.utxos; | |||||
// To see if any utxos exist in thisCurrentUtxoObjectUtxos that do not exist in thisPreviousUtxoObjectUtxos | |||||
// iterate over thisPreviousUtxoObjectUtxos for each utxo in thisCurrentUtxoObjectUtxos | |||||
for (let k = 0; k < thisCurrentUtxoObjectUtxos.length; k += 1) { | |||||
const thisCurrentUtxo = thisCurrentUtxoObjectUtxos[k]; | |||||
if ( | |||||
isExcludedUtxo( | |||||
thisCurrentUtxo, | |||||
thisPreviousUtxoObjectUtxos, | |||||
) | |||||
) { | |||||
// If thisCurrentUtxo was not in the corresponding previous utxos | |||||
// Then it was added | |||||
//utxosAdded.push(utxosAddedObject); | |||||
utxosAdded[j].utxos.push(thisCurrentUtxo); | |||||
utxosAddedFlag = true; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// If utxos were added, return them | |||||
if (utxosAddedFlag) { | |||||
return utxosAdded; | |||||
} | |||||
// Else return false | |||||
return utxosAddedFlag; | |||||
}; | |||||
export const whichUtxosWereConsumed = (previousUtxos, currentUtxos) => { | |||||
let utxosConsumedFlag = false; | |||||
const utxosConsumed = []; | |||||
// Iterate over previousUtxos | |||||
// For each previousUtxo -- does it exist in currentUtxos? | |||||
// If no, it's consumed | |||||
// Note that the inputs are arrays of arrays, model | |||||
/* | |||||
previousUtxos = [{address: 'string', utxos: []}, ...] | |||||
*/ | |||||
// Iterate over the previousUtxos array of {address: 'string', utxos: []} objects | |||||
for (let i = 0; i < previousUtxos.length; i += 1) { | |||||
// Take the first object | |||||
const thisPreviousUtxoObject = previousUtxos[i]; | |||||
const thisPreviousUtxoObjectAddress = thisPreviousUtxoObject.address; | |||||
const thisPreviousUtxoObjectUtxos = thisPreviousUtxoObject.utxos; | |||||
// Iterate over the currentUtxos array of {address: 'string', utxos: []} objects | |||||
for (let j = 0; j < currentUtxos.length; j += 1) { | |||||
const thisCurrentUtxoObject = currentUtxos[j]; | |||||
const thisCurrentUtxoObjectAddress = thisCurrentUtxoObject.address; | |||||
// When you find the utxos object at the same address | |||||
if ( | |||||
thisCurrentUtxoObjectAddress === thisPreviousUtxoObjectAddress | |||||
) { | |||||
// Create a utxosConsumedObject with the address | |||||
const utxosConsumedObject = { | |||||
address: thisCurrentUtxoObjectAddress, | |||||
utxos: [], | |||||
}; | |||||
utxosConsumed.push(utxosConsumedObject); | |||||
// Grab the currentUtxoObject utxos array. thisCurrentUtxoObjectUtxos has changed compared to thisPreviousUtxoObjectUtxos | |||||
const thisCurrentUtxoObjectUtxos = thisCurrentUtxoObject.utxos; | |||||
// To see if any utxos exist in thisPreviousUtxoObjectUtxos that do not exist in thisCurrentUtxoObjectUtxos | |||||
// iterate over thisCurrentUtxoObjectUtxos for each utxo in thisPreviousUtxoObjectUtxos | |||||
for ( | |||||
let k = 0; | |||||
k < thisPreviousUtxoObjectUtxos.length; | |||||
k += 1 | |||||
) { | |||||
const thisPreviousUtxo = thisPreviousUtxoObjectUtxos[k]; | |||||
// If thisPreviousUtxo was not in the corresponding current utxos | |||||
if ( | |||||
isExcludedUtxo( | |||||
thisPreviousUtxo, | |||||
thisCurrentUtxoObjectUtxos, | |||||
) | |||||
) { | |||||
// Then it was consumed | |||||
utxosConsumed[j].utxos.push(thisPreviousUtxo); | |||||
utxosConsumedFlag = true; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// If utxos were consumed, return them | |||||
if (utxosConsumedFlag) { | |||||
return utxosConsumed; | |||||
} | |||||
// Else return false | |||||
return utxosConsumedFlag; | |||||
}; | |||||
export const removeConsumedUtxos = (consumedUtxos, hydratedUtxoDetails) => { | |||||
let hydratedUtxoDetailsWithConsumedUtxosRemoved = hydratedUtxoDetails; | |||||
const slpUtxosArray = hydratedUtxoDetails.slpUtxos; | |||||
// Iterate over consumedUtxos | |||||
// Every utxo in consumedUtxos must be removed from hydratedUtxoDetails | |||||
for (let i = 0; i < consumedUtxos.length; i += 1) { | |||||
const thisConsumedUtxoObject = consumedUtxos[i]; // {address: 'string', utxos: [{},{},...{}]} | |||||
const thisConsumedUtxoObjectAddr = thisConsumedUtxoObject.address; | |||||
const thisConsumedUtxoObjectUtxoArray = thisConsumedUtxoObject.utxos; | |||||
for (let j = 0; j < thisConsumedUtxoObjectUtxoArray.length; j += 1) { | |||||
const thisConsumedUtxo = thisConsumedUtxoObjectUtxoArray[j]; | |||||
// Iterate through slpUtxosArray to find thisConsumedUtxo | |||||
slpUtxosArrayLoop: for ( | |||||
let k = 0; | |||||
k < slpUtxosArray.length; | |||||
k += 1 | |||||
) { | |||||
const thisSlpUtxosArrayUtxoObject = slpUtxosArray[k]; // {address: 'string', utxos: [{},{},...{}]} | |||||
const thisSlpUtxosArrayUtxoObjectAddr = | |||||
thisSlpUtxosArrayUtxoObject.address; | |||||
// If this address matches the address of the consumed utxo, check for a consumedUtxo match | |||||
// Note, slpUtxos may have many utxo objects with the same address, need to check them all until you find and remove this consumed utxo | |||||
if ( | |||||
thisConsumedUtxoObjectAddr === | |||||
thisSlpUtxosArrayUtxoObjectAddr | |||||
) { | |||||
const thisSlpUtxosArrayUtxoObjectUtxoArray = | |||||
thisSlpUtxosArrayUtxoObject.utxos; | |||||
// Iterate to find it and remove it | |||||
for ( | |||||
let m = 0; | |||||
m < thisSlpUtxosArrayUtxoObjectUtxoArray.length; | |||||
m += 1 | |||||
) { | |||||
const thisHydratedUtxo = | |||||
thisSlpUtxosArrayUtxoObjectUtxoArray[m]; | |||||
if ( | |||||
thisConsumedUtxo.tx_hash === | |||||
thisHydratedUtxo.tx_hash && | |||||
thisConsumedUtxo.tx_pos === | |||||
thisHydratedUtxo.tx_pos && | |||||
thisConsumedUtxo.value === thisHydratedUtxo.value | |||||
) { | |||||
// remove it | |||||
hydratedUtxoDetailsWithConsumedUtxosRemoved.slpUtxos[ | |||||
k | |||||
].utxos.splice(m, 1); | |||||
// go to the next consumedUtxo | |||||
break slpUtxosArrayLoop; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return hydratedUtxoDetailsWithConsumedUtxosRemoved; | |||||
}; | |||||
export const addNewHydratedUtxos = ( | |||||
addedHydratedUtxos, | |||||
hydratedUtxoDetails, | |||||
) => { | |||||
const theseAdditionalHydratedUtxos = addedHydratedUtxos.slpUtxos; | |||||
for (let i = 0; i < theseAdditionalHydratedUtxos.length; i += 1) { | |||||
const thisHydratedUtxoObj = theseAdditionalHydratedUtxos[i]; | |||||
hydratedUtxoDetails.slpUtxos.push(thisHydratedUtxoObj); | |||||
} | |||||
return hydratedUtxoDetails; | |||||
// Add hydrateUtxos(addedUtxos) to hydratedUtxoDetails | |||||
/* | |||||
e.g. add this | |||||
{ | |||||
"slpUtxos": | |||||
[ | |||||
{ | |||||
"utxos": [ | |||||
{ | |||||
"height": 725886, | |||||
"tx_hash": "29985c01444bf80ade764e5d40d7ec2c12317e03301243170139c75f20c51f78", | |||||
"tx_pos": 0, | |||||
"value": 3300, | |||||
"txid": "29985c01444bf80ade764e5d40d7ec2c12317e03301243170139c75f20c51f78", | |||||
"vout": 0, | |||||
"isValid": false | |||||
} | |||||
], | |||||
"address": "bitcoincash:qz2708636snqhsxu8wnlka78h6fdp77ar5ulhz04hr" | |||||
} | |||||
] | |||||
} | |||||
to this | |||||
{ | |||||
"slpUtxos": | |||||
[ | |||||
{ | |||||
"utxos": [ | |||||
{ | |||||
"height": 725886, | |||||
"tx_hash": "29985c01444bf80ade764e5d40d7ec2c12317e03301243170139c75f20c51f78", | |||||
"tx_pos": 0, | |||||
"value": 3300, | |||||
"txid": "29985c01444bf80ade764e5d40d7ec2c12317e03301243170139c75f20c51f78", | |||||
"vout": 0, | |||||
"isValid": false | |||||
} | |||||
... up to 20 | |||||
], | |||||
"address": "bitcoincash:qz2708636snqhsxu8wnlka78h6fdp77ar5ulhz04hr" | |||||
}, | |||||
{ | |||||
"utxos": [ | |||||
{ | |||||
"height": 725886, | |||||
"tx_hash": "29985c01444bf80ade764e5d40d7ec2c12317e03301243170139c75f20c51f78", | |||||
"tx_pos": 0, | |||||
"value": 3300, | |||||
"txid": "29985c01444bf80ade764e5d40d7ec2c12317e03301243170139c75f20c51f78", | |||||
"vout": 0, | |||||
"isValid": false | |||||
} | |||||
... up to 20 | |||||
], | |||||
"address": "bitcoincash:qz2708636snqhsxu8wnlka78h6fdp77ar5ulhz04hr" | |||||
} | |||||
, | |||||
... a bunch of these in batches of 20 | |||||
] | |||||
} | |||||
*/ | |||||
}; | |||||
export const getUtxoCount = utxos => { | |||||
// return how many utxos | |||||
/* | |||||
Both utxos and hydratedUtxoDetails.slpUtxos are build like so | |||||
[ | |||||
{ | |||||
address: 'string', | |||||
utxos: [{}, {}, {}...{}] | |||||
}, | |||||
{ | |||||
address: 'string', | |||||
utxos: [{}, {}, {}...{}] | |||||
}, | |||||
{ | |||||
address: 'string', | |||||
utxos: [{}, {}, {}...{}] | |||||
}, | |||||
] | |||||
We want a function that quickly determines how many utxos are here | |||||
*/ | |||||
let utxoCount = 0; | |||||
for (let i = 0; i < utxos.length; i += 1) { | |||||
const thisUtxoArrLength = utxos[i].utxos.length; | |||||
utxoCount += thisUtxoArrLength; | |||||
} | |||||
return utxoCount; | |||||
}; | |||||
export const areAllUtxosIncludedInIncrementallyHydratedUtxos = ( | |||||
utxos, | |||||
incrementallyHydratedUtxos, | |||||
) => { | |||||
let incrementallyHydratedUtxosIncludesAllUtxosInLatestUtxoApiFetch = false; | |||||
// check | |||||
const { slpUtxos } = incrementallyHydratedUtxos; | |||||
// Iterate over utxos array | |||||
for (let i = 0; i < utxos.length; i += 1) { | |||||
const thisUtxoObject = utxos[i]; | |||||
const thisUtxoObjectAddr = thisUtxoObject.address; | |||||
const thisUtxoObjectUtxos = thisUtxoObject.utxos; | |||||
let utxoFound; | |||||
for (let j = 0; j < thisUtxoObjectUtxos.length; j += 1) { | |||||
const thisUtxo = thisUtxoObjectUtxos[j]; | |||||
utxoFound = false; | |||||
// Now iterate over slpUtxos to find it | |||||
slpUtxosLoop: for (let k = 0; k < slpUtxos.length; k += 1) { | |||||
const thisSlpUtxosObject = slpUtxos[k]; | |||||
const thisSlpUtxosObjectAddr = thisSlpUtxosObject.address; | |||||
if (thisUtxoObjectAddr === thisSlpUtxosObjectAddr) { | |||||
const thisSlpUtxosObjectUtxos = thisSlpUtxosObject.utxos; | |||||
for ( | |||||
let m = 0; | |||||
m < thisSlpUtxosObjectUtxos.length; | |||||
m += 1 | |||||
) { | |||||
const thisSlpUtxo = thisSlpUtxosObjectUtxos[m]; | |||||
if ( | |||||
thisUtxo.tx_hash === thisSlpUtxo.tx_hash && | |||||
thisUtxo.tx_pos === thisSlpUtxo.tx_pos && | |||||
thisUtxo.value === thisSlpUtxo.value | |||||
) { | |||||
utxoFound = true; | |||||
// goto next utxo | |||||
break slpUtxosLoop; | |||||
} | |||||
} | |||||
} | |||||
if (k === slpUtxos.length - 1 && !utxoFound) { | |||||
// return false | |||||
return incrementallyHydratedUtxosIncludesAllUtxosInLatestUtxoApiFetch; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// It's possible that hydratedUtxoDetails includes every utxo from the utxos array, but for some reason also includes additional utxos | |||||
const utxosInUtxos = getUtxoCount(utxos); | |||||
const utxosInIncrementallyHydratedUtxos = getUtxoCount(slpUtxos); | |||||
if (utxosInUtxos !== utxosInIncrementallyHydratedUtxos) { | |||||
return incrementallyHydratedUtxosIncludesAllUtxosInLatestUtxoApiFetch; | |||||
} | |||||
// If you make it here, good to go | |||||
incrementallyHydratedUtxosIncludesAllUtxosInLatestUtxoApiFetch = true; | |||||
return incrementallyHydratedUtxosIncludesAllUtxosInLatestUtxoApiFetch; | |||||
}; |