-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
AKI-467: Added a config option to capture deployed contracts
-if set, this will capture all deployed contracts to the given directory -these can then be re-imported for offline analysis by the DirectoryDeployer tool
- Loading branch information
Showing
5 changed files
with
235 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
org.aion.avm.core/src/org/aion/avm/core/util/ContractCaptureTool.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package org.aion.avm.core.util; | ||
|
||
import java.io.File; | ||
import java.math.BigInteger; | ||
|
||
import org.aion.types.AionAddress; | ||
|
||
import i.RuntimeAssertionError; | ||
|
||
|
||
/** | ||
* Created as part of AKI-467 to capture contracts deployed on an AVM instance for offline analysis. | ||
*/ | ||
public class ContractCaptureTool { | ||
private static final String CODE_FILE_NAME = "code.jar"; | ||
private static final String ARGUMENTS_FILE_NAME = "arguments.bin"; | ||
private static final String CREATOR_FILE_NAME = "creator.bin"; | ||
private static final String NONCE_FILE_NAME = "creator_nonce.bin"; | ||
private static final String BLOCK_FILE_NAME = "block_height.txt"; | ||
|
||
private final File contractCaptureDirectory; | ||
|
||
public ContractCaptureTool(File contractCaptureDirectory) { | ||
this.contractCaptureDirectory = contractCaptureDirectory; | ||
} | ||
|
||
public void startup() { | ||
if (!this.contractCaptureDirectory.exists()) { | ||
this.contractCaptureDirectory.mkdirs(); | ||
} | ||
RuntimeAssertionError.assertTrue(this.contractCaptureDirectory.isDirectory()); | ||
} | ||
|
||
public void captureDeployment(long blockNumber, AionAddress senderAddress, AionAddress newContractAddress, BigInteger senderNonce, byte[] code, byte[] arguments) { | ||
String newContract = Helpers.bytesToHexString(newContractAddress.toByteArray()); | ||
File thisDirectory = new File(this.contractCaptureDirectory, newContract); | ||
thisDirectory.mkdirs(); | ||
Helpers.writeBytesToFile(code, new File(thisDirectory, CODE_FILE_NAME).getAbsolutePath()); | ||
if (null != arguments) { | ||
Helpers.writeBytesToFile(arguments, new File(thisDirectory, ARGUMENTS_FILE_NAME).getAbsolutePath()); | ||
} | ||
Helpers.writeBytesToFile(senderAddress.toByteArray(), new File(thisDirectory, CREATOR_FILE_NAME).getAbsolutePath()); | ||
Helpers.writeBytesToFile(senderNonce.toByteArray(), new File(thisDirectory, NONCE_FILE_NAME).getAbsolutePath()); | ||
Helpers.writeBytesToFile(Long.toString(blockNumber).getBytes(), new File(thisDirectory, BLOCK_FILE_NAME).getAbsolutePath()); | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
org.aion.avm.embed/src/org/aion/cli/DirectoryDeployer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package org.aion.cli; | ||
|
||
import org.aion.avm.core.AvmConfiguration; | ||
import org.aion.avm.core.AvmImpl; | ||
import org.aion.avm.core.AvmTransactionUtil; | ||
import org.aion.avm.core.CommonAvmFactory; | ||
import org.aion.avm.core.ExecutionType; | ||
import org.aion.avm.core.FutureResult; | ||
import org.aion.avm.core.IExternalCapabilities; | ||
import org.aion.avm.core.IExternalState; | ||
import org.aion.avm.core.util.Helpers; | ||
import org.aion.avm.embed.StandardCapabilities; | ||
import org.aion.avm.userlib.CodeAndArguments; | ||
import org.aion.kernel.*; | ||
import org.aion.types.AionAddress; | ||
import org.aion.types.Transaction; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.math.BigInteger; | ||
import java.nio.file.Files; | ||
|
||
|
||
/** | ||
* A tool to run our data collection histogram against pre-created contracts. | ||
* Point this at a directory containing code and argument data and it will deploy every contract found, with corresponding argument data. | ||
* The expected shape of the directory given is produced by the AvmConfiguration.contractCaptureDirectory option. | ||
* | ||
* NOTE: This currently deploys 1 contract at a time, in the order they are found in the directory, using the same emulated block. | ||
* In the future, this may be changed to deploy them in the order and blocks defined by the other meta-data. | ||
*/ | ||
public class DirectoryDeployer { | ||
private static AionAddress DEPLOYER = Helpers.randomAddress(); | ||
private static TestingBlock BLOCK = new TestingBlock(new byte[32], 1, DEPLOYER, System.currentTimeMillis(), new byte[0]); | ||
private static long ENERGY_LIMIT = 5_000_000L; | ||
private static long ENERGY_PRICE = 1L; | ||
|
||
public static void main(String[] args) { | ||
TestingState kernel = new TestingState(BLOCK); | ||
kernel.adjustBalance(DEPLOYER, new BigInteger("10000000000000000000000")); | ||
IExternalCapabilities capabilities = new StandardCapabilities(); | ||
AvmConfiguration config = new AvmConfiguration(); | ||
config.deploymentDataHistorgramOutput = System.out; | ||
AvmImpl avm = CommonAvmFactory.buildAvmInstanceForConfiguration(capabilities, config); | ||
|
||
File rootDirectory = new File(args[0]); | ||
assertTrue(rootDirectory.isDirectory()); | ||
int passCount = 0; | ||
int failCount = 0; | ||
for (String name : rootDirectory.list()) { | ||
try { | ||
Transaction transaction = createTransaction(kernel, new File(rootDirectory, name)); | ||
FutureResult[] futures = avm.run(kernel, new Transaction[] { transaction }, ExecutionType.ASSUME_MAINCHAIN, 0); | ||
boolean success = futures[0].getResult().transactionStatus.isSuccess(); | ||
if (success) { | ||
passCount += 1; | ||
} else { | ||
failCount += 1; | ||
} | ||
System.out.println(name + ": " + (success ? "PASS" : "FAIL")); | ||
} catch (IOException e) { | ||
System.err.println(name); | ||
e.printStackTrace(); | ||
System.exit(1); | ||
} | ||
} | ||
System.out.println("Attempted " + (passCount + failCount) + " deployments (" + passCount + " passed, " + failCount + " failed)"); | ||
avm.shutdown(); | ||
} | ||
|
||
|
||
private static Transaction createTransaction(IExternalState kernel, File directory) throws IOException { | ||
// For now, we will only load the code and the arguments. | ||
byte[] code = Files.readAllBytes(new File(directory, "code.jar").toPath()); | ||
File argFile = new File(directory, "arguments.bin"); | ||
byte[] args = argFile.exists() | ||
? Files.readAllBytes(argFile.toPath()) | ||
: new byte[0]; | ||
byte[] createData = new CodeAndArguments(code, args).encodeToBytes(); | ||
return AvmTransactionUtil.create(DEPLOYER, kernel.getNonce(DEPLOYER), BigInteger.ZERO, createData, ENERGY_LIMIT, ENERGY_PRICE); | ||
} | ||
|
||
private static void assertTrue(boolean flag) { | ||
// We use a private helper to manage the assertions since the JDK default disables them. | ||
if (!flag) { | ||
throw new AssertionError("Case must be true"); | ||
} | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
org.aion.avm.embed/test/org/aion/cli/DirectoryDeployerIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package org.aion.cli; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.PrintStream; | ||
import java.math.BigInteger; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import org.aion.kernel.TestingState; | ||
import org.aion.types.AionAddress; | ||
import org.aion.types.Transaction; | ||
import org.aion.types.TransactionResult; | ||
import org.aion.avm.core.AvmConfiguration; | ||
import org.aion.avm.core.AvmImpl; | ||
import org.aion.avm.core.AvmTransactionUtil; | ||
import org.aion.avm.core.CommonAvmFactory; | ||
import org.aion.avm.core.ExecutionType; | ||
import org.aion.avm.core.IExternalCapabilities; | ||
import org.aion.avm.core.dappreading.UserlibJarBuilder; | ||
import org.aion.avm.core.util.Helpers; | ||
import org.aion.avm.embed.StandardCapabilities; | ||
import org.aion.avm.userlib.CodeAndArguments; | ||
import org.aion.kernel.TestingBlock; | ||
import org.junit.Assert; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.TemporaryFolder; | ||
|
||
|
||
public class DirectoryDeployerIntegrationTest { | ||
private static AionAddress DEPLOYER = Helpers.randomAddress(); | ||
private static TestingBlock BLOCK = new TestingBlock(new byte[32], 1, DEPLOYER, System.currentTimeMillis(), new byte[0]); | ||
private static long ENERGY_LIMIT = 5_000_000L; | ||
private static long ENERGY_PRICE = 1L; | ||
|
||
@Rule | ||
public TemporaryFolder folder = new TemporaryFolder(); | ||
|
||
@Test | ||
public void compare() throws Exception { | ||
// The test we are interested in is is seeing if the histogram caused by deploying a basic contract is the same when in-process and out-of-process. | ||
ByteArrayOutputStream captureStream = new ByteArrayOutputStream(); | ||
TestingState kernel = new TestingState(BLOCK); | ||
kernel.adjustBalance(DEPLOYER, new BigInteger("10000000000000000000000")); | ||
IExternalCapabilities capabilities = new StandardCapabilities(); | ||
AvmConfiguration config = new AvmConfiguration(); | ||
config.deploymentDataHistorgramOutput = new PrintStream(captureStream); | ||
config.contractCaptureDirectory = folder.newFolder(); | ||
AvmImpl avm = CommonAvmFactory.buildAvmInstanceForConfiguration(capabilities, config); | ||
byte[] deployment = new CodeAndArguments(UserlibJarBuilder.buildJarForMainAndClassesAndUserlib(SimpleStackDemo.class), null).encodeToBytes(); | ||
Transaction transaction = AvmTransactionUtil.create(DEPLOYER, kernel.getNonce(DEPLOYER), BigInteger.ZERO, deployment, ENERGY_LIMIT, ENERGY_PRICE); | ||
TransactionResult result = avm.run(kernel, new Transaction[] {transaction}, ExecutionType.ASSUME_MAINCHAIN, kernel.getBlockNumber() - 1)[0].getResult(); | ||
Assert.assertTrue(result.transactionStatus.isSuccess()); | ||
AionAddress contractAddress = new AionAddress(result.copyOfTransactionOutput().orElseThrow()); | ||
String hexContract = Helpers.bytesToHexString(contractAddress.toByteArray()); | ||
avm.shutdown(); | ||
|
||
// By this point, we should be able to capture the output. | ||
String totalHistorgram = new String(captureStream.toByteArray(), StandardCharsets.UTF_8); | ||
|
||
// Make sure that we see the contract in that directory. | ||
Assert.assertEquals(1, config.contractCaptureDirectory.listFiles((file) -> file.getName().equals(hexContract)).length); | ||
|
||
// Now, invoke the DirectoryDeployer on the captured directory and make sure its output has this histogram at the end. | ||
PrintStream originalStdOut = System.out; | ||
ByteArrayOutputStream fakeStdOutBuffer = new ByteArrayOutputStream(); | ||
System.setOut(new PrintStream(fakeStdOutBuffer)); | ||
DirectoryDeployer.main(new String[] { config.contractCaptureDirectory.getAbsolutePath() }); | ||
System.setOut(originalStdOut); | ||
|
||
String fromCli = new String(fakeStdOutBuffer.toByteArray(), StandardCharsets.UTF_8); | ||
Assert.assertTrue(fromCli.endsWith(totalHistorgram)); | ||
} | ||
} |