ethereum - build unstoppable applications
智能合约
以太坊的智能合约具有动态脚本一样,它可以动态的执行,当我们在以太坊上发布了智能合约时,我们会知道发布的智能合约的地址,根据这个地址以及它定义的调用规范我们就可以调用这个合约,非常方便。
同样的我们也是通过几个问题来分析下智能合约的实现,
- 智能合约在什么时候执行的?
- 智能合约如何执行的?
智能合约在什么时候执行的
智能合约的执行应该有两种情况,一个是本节点通过json rpc收到transaction,另外一种是收到其他节点传播过来的transaction
- json rpc收到transaction
需要说明的是,ethereumj并没有实现json rpc server,不过提供了接口的实现,其中一个接口就是eth_sendTransaction,这个方法将收到的参数封装成transaction,再调用Ethereum的submitTransaction方法提交,后续做的事情就是合约的执行和继续传播了
@Override
public Future submitTransaction(Transaction transaction) {
#继续传播的任务
TransactionTask transactionTask = new TransactionTask(transaction, channelManager);
final Future> listFuture =
TransactionExecutor.instance.submitTransaction(transactionTask);
#transaction执行
pendingState.addPendingTransaction(transaction);
return new FutureAdapter>(listFuture) {
@Override
protected Transaction adapt(List adapteeResult) throws ExecutionException {
return adapteeResult.get(0);
}
};
}
- 节点传播过来的transaction
本节点收到其他节点传播过来的transaction后,使用processTransactions处理,如下
protected synchronized void processTransactions(TransactionsMessage msg) {
if(!processTransactions) {
return;
}
List txSet = msg.getTransactions();
#transaction执行
List newPending = pendingState.addPendingTransactions(txSet);
if (!newPending.isEmpty()) {
TransactionTask transactionTask = new TransactionTask(newPending, channel.getChannelManager(), channel);
#继续传播的任务 TransactionExecutor.instance.submitTransaction(transactionTask);
}
}
智能合约如何执行的
通过上面的分析我们已经知道合约的执行点,具体智能合约的执行那就应该在addPendingTransactions方法中,这个方法会循环验证所有的transaction,开始执行transaction
private boolean addPendingTransactionImpl(final Transaction tx) {
TransactionReceipt newReceipt = new TransactionReceipt();
newReceipt.setTransaction(tx);
String err = validate(tx);
TransactionReceipt txReceipt;
if (err != null) {
txReceipt = createDroppedReceipt(tx, err);
} else {
#执行transaction,下面分析
txReceipt = executeTx(tx);
}
if (!txReceipt.isValid()) {
fireTxUpdate(txReceipt, DROPPED, getBestBlock());
} else {
pendingTransactions.add(new PendingTransaction(tx, getBestBlock().getNumber()));
fireTxUpdate(txReceipt, NEW_PENDING, getBestBlock());
}
return txReceipt.isValid();
}
executeTx代码如下:
private TransactionReceipt executeTx(Transaction tx) {
logger.trace("Apply pending state tx: {}", Hex.toHexString(tx.getHash()));
Block best = getBestBlock();
TransactionExecutor executor = new TransactionExecutor(
tx, best.getCoinbase(), getRepository(),
blockStore, programInvokeFactory, createFakePendingBlock(), new EthereumListenerAdapter(), 0)
.withCommonConfig(commonConfig);
#验证gas是否足够,是否超出限值,nonce是否有效等,这些通过之后,readyToExecute设置为true
executor.init();
# 合约没有创建过则创建否则初始化vm以及program
executor.execute();
# 执行合约,如何执行的,下面分析
executor.go();
executor.finalization();
return executor.getReceipt();
}
executor.execute()方法初始化了vm以及program,在program中封装了合约代码
#合约代码所在的位置
byte[] code = track.getCode(targetAddress);
if (isEmpty(code)) {
m_endGas = m_endGas.subtract(BigInteger.valueOf(basicTxCost));
result.spendGas(basicTxCost);
} else {
ProgramInvoke programInvoke =
programInvokeFactory.createProgramInvoke(tx, currentBlock, cacheTrack, blockStore);
this.vm = new VM(config);
this.program = new Program(track.getCodeHash(targetAddress), code, programInvoke, tx, config).withCommonConfig(commonConfig);
}
executor.go()中通过解析code中的操作符,执行相应的操作,比如在智能合约中可以拿到COINBASE,那么在操作符中有相应的处理:
case COINBASE: {
DataWord coinbase = program.getCoinbase();
if (logger.isInfoEnabled())
hint = "coinbase: " + Hex.toHexString(coinbase.getLast20Bytes());
program.stackPush(coinbase);
program.step();
}
这些都是vm去实现的