CREATE op impl + UnitTest
This commit is contained in:
parent
b1a1a31e78
commit
9625dfe94b
|
@ -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;
|
||||
|
||||
|
@ -44,6 +45,22 @@ public class HashUtil {
|
|||
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)}.
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue