CREATE op impl + UnitTest

This commit is contained in:
romanman 2014-06-23 21:51:57 +01:00
parent b1a1a31e78
commit 9625dfe94b
5 changed files with 210 additions and 3 deletions

View File

@ -7,6 +7,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
import org.ethereum.util.Utils;
import org.spongycastle.util.encoders.Hex;
@ -43,6 +44,22 @@ public class HashUtil {
byte[] hash = sha3(input);
return copyOfRange(hash, 12, hash.length);
}
/**
* The way to calculate new address inside ethereum
*
* @param addr - creating addres
* @param nonce - nonce of creating address
* @return new address
*/
public static byte[] calcNewAddr(byte[] addr, byte[] nonce){
byte[] encSender = RLP.encodeElement(addr);
byte[] encNonce = RLP.encodeElement(nonce);
byte[] newAddress = HashUtil.sha3omit12(RLP.encodeList(encSender, encNonce));
return newAddress;
}
/**
* See {@link ByteUtil#doubleDigest(byte[], int, int)}.

View File

@ -2,8 +2,11 @@ package org.ethereum.vm;
import org.ethereum.core.AccountState;
import org.ethereum.core.ContractDetails;
import org.ethereum.crypto.HashUtil;
import org.ethereum.db.TrackDatabase;
import org.ethereum.manager.WorldManager;
import org.ethereum.trie.TrackTrie;
import org.ethereum.util.RLP;
import org.ethereum.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -50,7 +53,6 @@ public class Program {
this.invokeData = invokeData;
this.ops = ops;
if (invokeData.getStorage() != null){
storage = invokeData.getStorage();
}
@ -74,6 +76,11 @@ public class Program {
stack.push(stackWord);
}
public void stackPushOne(){
DataWord stackWord = new DataWord(1);
stack.push(stackWord);
}
public void stackPush(DataWord stackWord){
stack.push(stackWord);
@ -217,6 +224,91 @@ public class Program {
}
public void createContract(DataWord gas, DataWord memStart, DataWord memSize){
// 1. FETCH THE CODE FROM THE MEMORY
ByteBuffer programCode = memoryChunk(memStart, memSize);
TrackTrie stateDB = new TrackTrie( result.getStateDb() );
TrackDatabase chainDB = new TrackDatabase( result.getChainDb() );
TrackDatabase detailDB = new TrackDatabase( result.getDetailDB() );
detailDB.startTrack();
chainDB.startTrack();
stateDB.startTrack();
if (logger.isInfoEnabled())
logger.info("creating a new contract");
byte[] senderAddress = this.getOwnerAddress().getNoLeadZeroesData();
byte[] data = stateDB.get( senderAddress );
AccountState senderState = new AccountState(data);
// 2.1 PERFORM THE GAS VALUE TX
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
if (this.getGas().longValue() - gas.longValue() < 0 ){
logger.info("No gas for the internal CREATE op, \n" +
"contract={}",
Hex.toHexString(senderAddress));
this.stackPushZero();
return;
}
// 2.2 CREATE THE CONTRACT ADDRESS
byte[] nonce = senderState.getNonce().toByteArray();
byte[] newAddress = HashUtil.calcNewAddr(this.getOwnerAddress().getNoLeadZeroesData(), nonce);
// 2.3 UPDATE THE NONCE
// (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
senderState.incrementNonce();
// 3. COOK THE INVOKE AND EXECUTE
ProgramInvoke programInvoke =
ProgramInvokeFactory.createProgramInvoke(this, DataWord.ZERO, null,
DataWord.ZERO, gas, BigInteger.ZERO,
null,
detailDB, chainDB, stateDB);
VM vm = new VM();
Program program = new Program(programCode.array(), programInvoke);
vm.play(program);
ProgramResult result = program.getResult();
if (result.getException() != null &&
result.getException() instanceof Program.OutOfGasException){
logger.info("contract run halted by OutOfGas: new contract init ={}" , Hex.toHexString(newAddress));
detailDB.rollbackTrack();
chainDB.rollbackTrack();
stateDB.rollbackTrack();
stackPushZero();
return;
}
// 4. CREATE THE CONTRACT OUT OF RETURN
byte[] code = result.getHReturn().array();
byte[] keyCode = HashUtil.sha3(code);
ContractDetails contractDetails = new ContractDetails(program.storage);
AccountState state = new AccountState();
state.setCodeHash(keyCode);
stateDB.update(newAddress, state.getEncoded());
chainDB.put(keyCode, code);
detailDB.put(newAddress, contractDetails.getEncoded());
// IN SUCCESS PUSH THE ADDRESS IN THE STACK
stackPush(new DataWord(newAddress));
detailDB.commitTrack();
chainDB.commitTrack();
stateDB.commitTrack();
}
/**
* That method implement internal calls
* and code invocations
@ -253,6 +345,7 @@ public class Program {
receiverState = new AccountState(accountData);
}
// FETCH THE CODE
byte[] programCode = result.getChainDb().get(receiverState.getCodeHash());
if (programCode != null && programCode.length != 0){
@ -338,6 +431,9 @@ public class Program {
}
}
// 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
stackPushOne();
detailDB.commitTrack();
chainDB.commitTrack();
stateDB.commitTrack();

View File

@ -169,6 +169,7 @@ public class ProgramInvokeFactory {
Map<DataWord, DataWord> storage = storageIn;
if (logger.isInfoEnabled()){
logger.info("Program invocation: \n" +
"address={}\n" +
"origin={}\n" +
@ -192,7 +193,7 @@ public class ProgramInvokeFactory {
new BigInteger(gasPrice.getData()).longValue(),
new BigInteger(gas.getData()).longValue(),
Hex.toHexString(callValue.getData()),
Hex.toHexString(data),
data == null ? "null": Hex.toHexString(data),
Hex.toHexString(lastHash.getData()),
Hex.toHexString(coinbase.getData()),
Hex.toHexString(timestamp.getData()),

View File

@ -513,7 +513,7 @@ public class VM {
DataWord inOffset = program.stackPop();
DataWord inSize = program.stackPop();
// todo: implement contract creation
program.createContract(gas, inOffset, inSize);
program.step();
} break;

View File

@ -216,6 +216,9 @@ public class VMComplexTest {
assertEquals(expectedVal_1, value_1.longValue());
assertEquals(expectedVal_2, value_2.longValue());
// todo: check that the value pushed after exec is 1
}
@ -333,6 +336,96 @@ public class VMComplexTest {
assertEquals(expectedVal_5, value5.longValue());
assertEquals(expectedVal_6, value6.longValue());
// todo: check that the value pushed after exec is 1
}
@Test // CREATE magic
public void test4(){
/**
* #The code will run
* ------------------
contract A: 77045e71a7a2c50903d88e564cd72fab11e82051
-----------
a = 0x7f60c860005461012c6020540000000000000000000000000000000000000000
b = 0x0060005460206000f20000000000000000000000000000000000000000000000
create(100, 0 41)
contract B: (the contract to be created the addr will be defined to: 8e45367623a2865132d9bf875d5cfa31b9a0cd94)
-----------
a = 200
b = 300
*/
// Set contract into Database
String callerAddr = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826";
String contractA_addr = "77045e71a7a2c50903d88e564cd72fab11e82051";
String code_a = "7f7f60c860005461012c602054000000000000" +
"00000000000000000000000000006000547e60" +
"005460206000f2000000000000000000000000" +
"0000000000000000000000602054602960006064f0";
byte[] caller_addr_bytes = Hex.decode(callerAddr);
byte[] contractA_addr_bytes = Hex.decode(contractA_addr);
byte[] codeA = Hex.decode(code_a);
byte[] codeA_Key = HashUtil.sha3(codeA);
AccountState accountState_a = new AccountState();
accountState_a.setCodeHash(codeA_Key);
WorldManager.instance.worldState.update(contractA_addr_bytes, accountState_a.getEncoded());
AccountState callerAcountState = new AccountState();
callerAcountState.addToBalance(new BigInteger("100000000000000000000"));
WorldManager.instance.worldState.update(caller_addr_bytes, callerAcountState.getEncoded());
WorldManager.instance.chainDB.put(codeA_Key, codeA);
TrackTrie stateDB = new TrackTrie(WorldManager.instance.worldState);
TrackDatabase chainDb = new TrackDatabase(WorldManager.instance.chainDB);
TrackDatabase detaildDB = new TrackDatabase(WorldManager.instance.detaildDB);
ProgramInvokeMockImpl pi = new ProgramInvokeMockImpl();
pi.setDetaildDB(detaildDB);
pi.setChainDb(chainDb);
pi.setStateDB(stateDB);
pi.setDetails(null);
pi.setOwnerAddress(contractA_addr);
// ****************** //
// Play the program //
// ****************** //
VM vm = new VM();
Program program = new Program(codeA, pi);
try {
while(!program.isStopped())
vm.step(program);
} catch (RuntimeException e) {
program.setRuntimeFailure(e);
}
System.out.println();
System.out.println("============ Results ============");
AccountState as =
new AccountState(WorldManager.instance.worldState.get(
Hex.decode( contractA_addr) ));
System.out.println("*** Used gas: " + program.result.getGasUsed());
// todo: check that the value pushed after exec is the new address
}