/* * Copyright (c) [2016] [ <ether.camp> ] * This file is part of the ethereumJ library. * * The ethereumJ library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ethereumJ library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>. */ package org.ethereum.core; import org.ethereum.vm.DataWord; import org.ethereum.vm.LogInfo; import org.ethereum.vm.program.InternalTransaction; import org.junit.Test; import java.math.BigInteger; import java.util.*; import static org.apache.commons.collections4.CollectionUtils.size; import static org.ethereum.util.ByteUtil.toHexString; import static org.junit.Assert.*; public class TransactionExecutionSummaryTest { @Test public void testRlpEncoding() { Transaction tx = randomTransaction(); Set<DataWord> deleteAccounts = new HashSet<>(randomDataWords(10)); List<LogInfo> logs = randomLogsInfo(5); final Map<DataWord, DataWord> readOnly = randomStorageEntries(20); final Map<DataWord, DataWord> changed = randomStorageEntries(5); Map<DataWord, DataWord> all = new HashMap<DataWord, DataWord>() {{ putAll(readOnly); putAll(changed); }}; BigInteger gasLeftover = new BigInteger("123"); BigInteger gasRefund = new BigInteger("125"); BigInteger gasUsed = new BigInteger("556"); final int nestedLevelCount = 5000; final int countByLevel = 1; List<InternalTransaction> internalTransactions = randomInternalTransactions(tx, nestedLevelCount, countByLevel); byte[] result = randomBytes(32); byte[] encoded = new TransactionExecutionSummary.Builder(tx) .deletedAccounts(deleteAccounts) .logs(logs) .touchedStorage(all, changed) .gasLeftover(gasLeftover) .gasRefund(gasRefund) .gasUsed(gasUsed) .internalTransactions(internalTransactions) .result(result) .build() .getEncoded(); TransactionExecutionSummary summary = new TransactionExecutionSummary(encoded); assertArrayEquals(tx.getHash(), summary.getTransactionHash()); assertEquals(size(deleteAccounts), size(summary.getDeletedAccounts())); for (DataWord account : summary.getDeletedAccounts()) { assertTrue(deleteAccounts.contains(account)); } assertEquals(size(logs), size(summary.getLogs())); for (int i = 0; i < logs.size(); i++) { assertLogInfoEquals(logs.get(i), summary.getLogs().get(i)); } assertStorageEquals(all, summary.getTouchedStorage().getAll()); assertStorageEquals(changed, summary.getTouchedStorage().getChanged()); assertStorageEquals(readOnly, summary.getTouchedStorage().getReadOnly()); assertEquals(gasRefund, summary.getGasRefund()); assertEquals(gasLeftover, summary.getGasLeftover()); assertEquals(gasUsed, summary.getGasUsed()); assertEquals(nestedLevelCount * countByLevel, size(internalTransactions)); assertArrayEquals(result, summary.getResult()); } private static void assertStorageEquals(Map<DataWord, DataWord> expected, Map<DataWord, DataWord> actual) { assertNotNull(expected); assertNotNull(actual); assertEquals(expected.size(), actual.size()); for (DataWord key : expected.keySet()) { DataWord actualValue = actual.get(key); assertNotNull(actualValue); assertArrayEquals(expected.get(key).getData(), actualValue.getData()); } } private static void assertLogInfoEquals(LogInfo expected, LogInfo actual) { assertNotNull(expected); assertNotNull(actual); assertArrayEquals(expected.getAddress(), actual.getAddress()); assertEquals(size(expected.getTopics()), size(actual.getTopics())); for (int i = 0; i < size(expected.getTopics()); i++) { assertArrayEquals(expected.getTopics().get(i).getData(), actual.getTopics().get(i).getData()); } assertArrayEquals(expected.getData(), actual.getData()); } private static Map<DataWord, DataWord> randomStorageEntries(int count) { Map<DataWord, DataWord> result = new HashMap<>(); for (int i = 0; i < count; i++) { result.put(randomDataWord(), randomDataWord()); } return result; } private static LogInfo randomLogInfo() { return new LogInfo(randomBytes(20), randomDataWords(5), randomBytes(8)); } private static List<LogInfo> randomLogsInfo(int count) { List<LogInfo> result = new ArrayList<>(count); for (int i = 0; i < count; i++) { result.add(randomLogInfo()); } return result; } private static DataWord randomDataWord() { return new DataWord(randomBytes(32)); } private static DataWord randomAddress() { return new DataWord(randomBytes(20)); } private static List<DataWord> randomDataWords(int count) { List<DataWord> result = new ArrayList<>(count); for (int i = 0; i < count; i++) { result.add(randomDataWord()); } return result; } private static InternalTransaction randomInternalTransaction(Transaction parent, int deep, int index) { return new InternalTransaction(parent.getHash(), deep, index, randomBytes(1), DataWord.ZERO, DataWord.ZERO, parent.getReceiveAddress(), randomBytes(20), randomBytes(2), randomBytes(64), "test note"); } private static List<InternalTransaction> randomInternalTransactions(Transaction parent, int nestedLevelCount, int countByLevel) { List<InternalTransaction> result = new ArrayList<>(); if (nestedLevelCount > 0) { for (int index = 0; index < countByLevel; index++) { result.add(randomInternalTransaction(parent, nestedLevelCount, index)); } result.addAll(0, randomInternalTransactions(result.get(result.size() - 1), nestedLevelCount - 1, countByLevel)); } return result; } private static Transaction randomTransaction() { Transaction transaction = Transaction.createDefault(toHexString(randomBytes(20)), new BigInteger(randomBytes(2)), new BigInteger(randomBytes(1)), null); transaction.sign(randomBytes(32)); return transaction; } private static byte[] randomBytes(int len) { byte[] bytes = new byte[len]; new Random().nextBytes(bytes); return bytes; } }