use of neo.model.core.TransactionOutput in project neo-java by coranos.
the class BlockDbMapDbImpl method updateAssetAndValueByAccountMap.
/**
* updates the asset and value by account map.
*
* @param block
* the block to update.
* @param reverse
* if true, reverse the update.
*/
private void updateAssetAndValueByAccountMap(final Block block, final boolean reverse) {
final BTreeMap<byte[], byte[]> assetAndValueByAccountMap = getAssetAndValueByAccountMap();
final BTreeMap<byte[], byte[]> transactionByAccountAndIndexMap = getTransactionByAccountAndIndexMap();
final BTreeMap<byte[], Long> transactionByAccountMaxIndexMap = getTransactionByAccountMaxIndexMap();
final BTreeMap<byte[], Boolean> transactionOutputSpentStateMap = getTransactionOutputSpentStateMap();
LOG.debug("updateAssetAndValueByAccountMap STARTED block;{};reverse;{};numberOfAccounts:{}", block.getIndexAsLong(), reverse, assetAndValueByAccountMap.size());
for (final Transaction t : block.getTransactionList()) {
if (LOG.isDebugEnabled()) {
LOG.debug("updateAssetAndValueByAccountMap INTERIM tx:{}", t.getHash());
}
for (final CoinReference cr : t.inputs) {
if (LOG.isDebugEnabled()) {
LOG.debug("updateAssetAndValueByAccountMap INTERIM cr:{}", cr.toJSONObject());
}
final byte[] crBa = cr.toByteArray();
if (!transactionOutputSpentStateMap.containsKey(crBa)) {
throw new RuntimeException("referenced transaction output was never a transaction input:" + cr);
}
final boolean oldSpendState;
final boolean newSpendState;
if (reverse) {
oldSpendState = true;
newSpendState = false;
} else {
oldSpendState = false;
newSpendState = true;
}
if (transactionOutputSpentStateMap.get(crBa) == oldSpendState) {
transactionOutputSpentStateMap.put(crBa, newSpendState);
final UInt256 prevHashReversed = cr.prevHash.reverse();
final Transaction tiTx = getTransactionWithHash(prevHashReversed);
if (tiTx == null) {
throw new RuntimeException("no transaction with prevHash:" + prevHashReversed + " in block[1] " + block.hash + " index[1] " + block.getIndexAsLong());
}
final int prevIndex = cr.prevIndex.asInt();
if (prevIndex >= tiTx.outputs.size()) {
throw new RuntimeException("prevIndex:" + prevIndex + " exceeds output size:" + tiTx.outputs.size() + "; in block[2] " + block.hash + " index[2] " + block.getIndexAsLong());
}
final TransactionOutput ti = tiTx.outputs.get(prevIndex);
final UInt160 input = ti.scriptHash;
final byte[] inputBa = input.toByteArray();
final Map<UInt256, Fixed8> accountAssetValueMap = ensureAccountExists(assetAndValueByAccountMap, inputBa);
if (LOG.isDebugEnabled()) {
LOG.debug("TI beforeMap {}", accountAssetValueMap);
}
if (!accountAssetValueMap.containsKey(ti.assetId)) {
accountAssetValueMap.put(ti.assetId, ModelUtil.FIXED8_ZERO);
}
final Fixed8 oldValue = accountAssetValueMap.get(ti.assetId);
final Fixed8 newValue;
if (reverse) {
newValue = ModelUtil.add(ti.value, oldValue);
} else {
newValue = ModelUtil.subtract(ti.value, oldValue);
}
if (LOG.isDebugEnabled()) {
LOG.debug("updateAssetAndValueByAccountMap INTERIM input;{};", ModelUtil.scriptHashToAddress(input));
LOG.debug("updateAssetAndValueByAccountMap INTERIM ti.assetId:{} oldValue:{};", ti.assetId, oldValue);
LOG.debug("updateAssetAndValueByAccountMap INTERIM ti.assetId:{} to.value:{};", ti.assetId, ti.value);
LOG.debug("updateAssetAndValueByAccountMap INTERIM ti.assetId:{} newValue:{};", ti.assetId, newValue);
}
if (newValue.equals(ModelUtil.FIXED8_ZERO)) {
accountAssetValueMap.remove(ti.assetId);
} else {
accountAssetValueMap.put(ti.assetId, newValue);
}
if (accountAssetValueMap.isEmpty()) {
assetAndValueByAccountMap.remove(inputBa);
} else {
putAssetValueMap(assetAndValueByAccountMap, inputBa, accountAssetValueMap);
}
if (LOG.isDebugEnabled()) {
LOG.debug("TI afterMap {}", ensureAccountExists(assetAndValueByAccountMap, inputBa));
}
} else {
if (reverse) {
throw new RuntimeException("referenced transaction output is not already spent:" + cr);
} else {
throw new RuntimeException("referenced transaction output is already spent:" + cr);
}
}
}
try {
for (int outputIx = 0; outputIx < t.outputs.size(); outputIx++) {
final TransactionOutput to = t.outputs.get(outputIx);
if (LOG.isDebugEnabled()) {
LOG.debug("updateAssetAndValueByAccountMap INTERIM to:{}", to.toJSONObject());
}
final UInt160 output = to.scriptHash;
final byte[] outputBa = output.toByteArray();
final Map<UInt256, Fixed8> accountAssetValueMap = ensureAccountExists(assetAndValueByAccountMap, outputBa);
if (LOG.isDebugEnabled()) {
LOG.debug("TO beforeMap {}", accountAssetValueMap);
}
if (!accountAssetValueMap.containsKey(to.assetId)) {
accountAssetValueMap.put(to.assetId, ModelUtil.FIXED8_ZERO);
}
final Fixed8 oldValue = accountAssetValueMap.get(to.assetId);
if (LOG.isDebugEnabled()) {
LOG.debug("updateAssetAndValueByAccountMap INTERIM output;{};", ModelUtil.scriptHashToAddress(output));
LOG.debug("updateAssetAndValueByAccountMap INTERIM to.assetId:{} oldValue:{};", to.assetId, oldValue);
LOG.debug("updateAssetAndValueByAccountMap INTERIM to.assetId:{} to.value:{};", to.assetId, to.value);
}
final Fixed8 newValue;
if (reverse) {
newValue = ModelUtil.subtract(to.value, oldValue);
} else {
newValue = ModelUtil.add(oldValue, to.value);
}
accountAssetValueMap.put(to.assetId, newValue);
if (accountAssetValueMap.isEmpty()) {
assetAndValueByAccountMap.remove(outputBa);
} else {
putAssetValueMap(assetAndValueByAccountMap, outputBa, accountAssetValueMap);
}
if (LOG.isDebugEnabled()) {
LOG.debug("updateAssetAndValueByAccountMap INTERIM to.assetId:{} newValue:{};", to.assetId, newValue);
LOG.debug("TO afterMap {}", ensureAccountExists(assetAndValueByAccountMap, outputBa));
}
final CoinReference cr = new CoinReference(t.getHash().reverse(), new UInt16(outputIx));
if (reverse) {
transactionOutputSpentStateMap.remove(cr.toByteArray());
} else {
transactionOutputSpentStateMap.put(cr.toByteArray(), false);
}
}
final byte[] tBa = t.toByteArray();
final long index;
if (transactionByAccountMaxIndexMap.containsKey(tBa)) {
index = transactionByAccountMaxIndexMap.get(tBa);
} else {
index = 0;
}
transactionByAccountMaxIndexMap.put(tBa, index + 1);
final byte[] accountKeyBa = getAccountKey(tBa, index);
transactionByAccountAndIndexMap.put(accountKeyBa, tBa);
} catch (final RuntimeException e) {
final String msg = "error processing transaction type " + t.type + " hash " + t.getHash();
throw new RuntimeException(msg, e);
}
}
LOG.debug("updateAssetAndValueByAccountMap SUCCESS block;{};numberOfAccounts:{}", block.getIndexAsLong(), assetAndValueByAccountMap.size());
}
use of neo.model.core.TransactionOutput in project neo-java by coranos.
the class BlockDbMapDbImpl method getUnspentTransactionOutputListMap.
@Override
public Map<UInt256, Map<TransactionOutput, CoinReference>> getUnspentTransactionOutputListMap(final UInt160 account) {
final List<Transaction> transactionList = getTransactionWithAccountList(account);
final BTreeMap<byte[], Boolean> transactionOutputSpentStateMap = getTransactionOutputSpentStateMap();
final Map<UInt256, Map<TransactionOutput, CoinReference>> assetIdTxoMap = new TreeMap<>();
final BTreeMap<byte[], byte[]> txKeyByHashMap = getTransactionKeyByTransactionHashMap();
for (final Transaction transaction : transactionList) {
final byte[] hashBa = transaction.getHash().toByteArray();
final byte[] txKeyBa = txKeyByHashMap.get(hashBa);
for (final TransactionOutput to : transaction.outputs) {
if (to.scriptHash.equals(account)) {
final byte[] toBa = to.toByteArray();
if (transactionOutputSpentStateMap.containsKey(toBa)) {
if (transactionOutputSpentStateMap.get(toBa) == true) {
if (!assetIdTxoMap.containsKey(to.assetId)) {
assetIdTxoMap.put(to.assetId, new TreeMap<>());
}
final int txIx = getTransactionIndexInBlockFromTransactionKey(txKeyBa);
final CoinReference cr = new CoinReference(transaction.getHash(), new UInt16(txIx));
assetIdTxoMap.get(to.assetId).put(to, cr);
}
}
}
}
}
return assetIdTxoMap;
}
use of neo.model.core.TransactionOutput in project neo-java by coranos.
the class RpcServerUtil method getAddressAssetMap.
/**
* returns the address asset map.
*
* @param blockDb
* the block database to use.
* @param transaction
* the transaction to use.
* @return the address asset map.
*/
public static Map<UInt160, Map<UInt256, Long>> getAddressAssetMap(final BlockDb blockDb, final Transaction transaction) {
final Map<UInt160, Map<UInt256, Long>> friendAssetMap = new TreeMap<>();
for (final CoinReference cr : transaction.inputs) {
final UInt256 prevHashReversed = cr.prevHash.reverse();
final Transaction tiTx = blockDb.getTransactionWithHash(prevHashReversed);
if (tiTx == null) {
throw new RuntimeException("no transaction with prevHash:" + prevHashReversed);
}
final TransactionOutput ti = tiTx.outputs.get(cr.prevIndex.asInt());
final UInt160 input = ti.scriptHash;
if ((ti.assetId.equals(ModelUtil.NEO_HASH)) || (ti.assetId.equals(ModelUtil.GAS_HASH))) {
MapUtil.increment(friendAssetMap, input, ti.assetId, ti.value.value, TreeMap.class);
}
}
for (final TransactionOutput to : transaction.outputs) {
final UInt160 output = to.scriptHash;
if ((to.assetId.equals(ModelUtil.NEO_HASH)) || (to.assetId.equals(ModelUtil.GAS_HASH))) {
MapUtil.increment(friendAssetMap, output, to.assetId, -to.value.value, TreeMap.class);
}
}
return friendAssetMap;
}
use of neo.model.core.TransactionOutput in project neo-java by coranos.
the class RpcServerUtil method toUnspentJSONArray.
/**
* converts a map of TransactionOutputs and CoinReferences to a json array of
* unspent transaction outputs.
*
* @param map
* the map to use.
* @param withDecimals
* if true, the value should have decimals.
* @return the json array of unspent transaction outputs.
*/
private static JSONArray toUnspentJSONArray(final Map<TransactionOutput, CoinReference> map, final boolean withDecimals) {
if (map == null) {
return null;
}
final JSONArray array = new JSONArray();
for (final TransactionOutput output : map.keySet()) {
final CoinReference cr = map.get(output);
final JSONObject elt = toUnspentJSONObject(withDecimals, output, cr);
array.put(elt);
}
return array;
}
use of neo.model.core.TransactionOutput in project neo-java by coranos.
the class TestCoreToJson method test010TransactionOutput.
/**
* test TransactionOutput.
*/
@Test
public void test010TransactionOutput() {
final TransactionOutput transactionOutput = MockUtil.getTransactionOutput000();
final String expectedStrRaw = TestUtil.getJsonTestResourceAsString(TEST_PACKAGE, getClass().getSimpleName(), "test010TransactionOutput");
final String expectedStr = new JSONObject(expectedStrRaw).toString();
final String actualStr = transactionOutput.toString();
Assert.assertEquals(TestUtil.RESPONSES_MUST_MATCH, expectedStr, actualStr);
}
Aggregations