All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.hotmoka.node.local.internal.tries.TrieOfHistories Maven / Gradle / Ivy

/*
Copyright 2021 Fausto Spoto

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package io.hotmoka.node.local.internal.tries;

import java.security.NoSuchAlgorithmException;
import java.util.Optional;
import java.util.stream.Stream;

import io.hotmoka.crypto.HashingAlgorithms;
import io.hotmoka.crypto.api.HashingAlgorithm;
import io.hotmoka.node.TransactionReferences;
import io.hotmoka.node.api.transactions.TransactionReference;
import io.hotmoka.node.api.values.StorageReference;
import io.hotmoka.patricia.AbstractPatriciaTrie;
import io.hotmoka.patricia.api.KeyValueStore;
import io.hotmoka.patricia.api.TrieException;

/**
 * A map from storage references to an array of transaction references (their history),
 * backed by a Merkle-Patricia trie.
 * It uses sha256 as hashing algorithm for the trie's nodes and an array of 0's to represent
 * the empty trie.
 */
public class TrieOfHistories extends AbstractPatriciaTrie, TrieOfHistories> {

	/**
	 * Builds a Merkle-Patricia trie that maps references to storage references into
	 * an array of transaction references (their history).
	 * 
	 * @param store the supporting key/value store
	 * @param root the root of the trie to check out; use empty to create the empty trie
	 */
	public TrieOfHistories(KeyValueStore store, byte[] root) throws TrieException {
		super(store, root, sha256().getHasher(StorageReference::toByteArrayWithoutSelector),
			sha256(), new byte[32], TrieOfHistories::historyToBytes, TrieOfHistories::bytesToHistory);
	}

	private static byte[] historyToBytes(Stream history) {
		TransactionReference[] toConcat = history.toArray(TransactionReference[]::new);
		int requestHashLength = TransactionReference.REQUEST_HASH_LENGTH;
		byte[] result = new byte[toConcat.length * requestHashLength];

		int pos = 0;
		for (TransactionReference reference: toConcat) {
			System.arraycopy(reference.getHash(), 0, result, pos, requestHashLength);
			pos += requestHashLength;
		}

		return result;
	}

	private static Stream bytesToHistory(byte[] bytes) {
		int requestHashLength = TransactionReference.REQUEST_HASH_LENGTH;
		var references = new TransactionReference[bytes.length / requestHashLength];
		for (int index = 0, pos = 0; pos < bytes.length; pos += requestHashLength, index++) {
			var hash = new byte[requestHashLength];
			System.arraycopy(bytes, pos, hash, 0, requestHashLength);
			references[index] = TransactionReferences.of(hash);
		}
		
		return Stream.of(references);
	}

	private TrieOfHistories(TrieOfHistories cloned, byte[] root) throws TrieException {
		super(cloned, root);
	}

	private static HashingAlgorithm sha256() throws TrieException {
		try {
			return HashingAlgorithms.sha256();
		}
		catch (NoSuchAlgorithmException e) {
			throw new TrieException(e);
		}
	}

	@Override
	public Optional> get(StorageReference key) throws TrieException {
		Optional> result = super.get(key);
		if (result.isEmpty())
			return Optional.empty();

		TransactionReference[] transactions = result.get().toArray(TransactionReference[]::new);
		// histories always end with the transaction that created the object,
		// hence with the transaction of the storage reference of the object
		var withLast = new TransactionReference[transactions.length + 1];
		System.arraycopy(transactions, 0, withLast, 0, transactions.length);
		withLast[transactions.length] = key.getTransaction();
		return Optional.of(Stream.of(withLast));
	}

	@Override
	public TrieOfHistories put(StorageReference key, Stream history) throws TrieException {
		// we do not keep the last transaction, since the history of an object always ends
		// with the transaction that created the object, that is, with the same transaction
		// of the storage reference of the object
		var transactionsAsArray = history.toArray(TransactionReference[]::new);
		var withoutLast = new TransactionReference[transactionsAsArray.length - 1];
		System.arraycopy(transactionsAsArray, 0, withoutLast, 0, withoutLast.length);
		return super.put(key, Stream.of(withoutLast));
	}

	@Override
	public TrieOfHistories checkoutAt(byte[] root) throws TrieException {
		return new TrieOfHistories(this, root);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy