Skip to content

Commit

Permalink
import graalvm and support javascript contract language
Browse files Browse the repository at this point in the history
  • Loading branch information
imuge committed Mar 3, 2022
1 parent b43f3c9 commit aaf58e8
Show file tree
Hide file tree
Showing 29 changed files with 757 additions and 692 deletions.
12 changes: 12 additions & 0 deletions contract/contract-jvm/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@
<artifactId>spring-boot-starter-log4j2</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
</dependency>
<dependency>
<groupId>org.graalvm.truffle</groupId>
<artifactId>truffle-api</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,111 +1,74 @@
package com.jd.blockchain.contract.jvm;

import java.lang.reflect.Method;

import com.jd.blockchain.contract.ContractEventContext;
import com.jd.blockchain.contract.engine.ContractCode;
import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.ContractExecuteException;
import com.jd.blockchain.ledger.LedgerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import com.jd.blockchain.contract.ContractEventContext;
import com.jd.blockchain.contract.ContractException;
import com.jd.blockchain.contract.EventProcessingAware;
import com.jd.blockchain.contract.engine.ContractCode;
import com.jd.blockchain.ledger.BytesValue;
import com.jd.blockchain.ledger.BytesValueEncoding;
import com.jd.blockchain.ledger.BytesValueList;

import utils.Bytes;

/**
* @author huanghaiquan
*
*/
public abstract class AbstractContractCode implements ContractCode {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractContractCode.class);
private Bytes address;
private long version;

private ContractDefinition contractDefinition;

public AbstractContractCode(Bytes address, long version, ContractDefinition contractDefinition) {
this.address = address;
this.version = version;
this.contractDefinition = contractDefinition;
}

public ContractDefinition getContractDefinition() {
return contractDefinition;
}

@Override
public Bytes getAddress() {
return address;
}

@Override
public long getVersion() {
return version;
}

@Override
public BytesValue processEvent(ContractEventContext eventContext) {
EventProcessingAware evtProcAwire = null;
Object retn = null;
Method handleMethod = null;
LedgerException error = null;
try {
// 执行预处理;
Object contractInstance = getContractInstance();
if (contractInstance instanceof EventProcessingAware) {
evtProcAwire = (EventProcessingAware) contractInstance;
}

if (evtProcAwire != null) {
evtProcAwire.beforeEvent(eventContext);
}

// 反序列化参数;
handleMethod = contractDefinition.getType().getHandleMethod(eventContext.getEvent());

if (handleMethod == null) {
throw new ContractException(
String.format("Contract[%s:%s] has no handle method to handle event[%s]!", address.toString(),
contractDefinition.getType().getName(), eventContext.getEvent()));
}

BytesValueList bytesValues = eventContext.getArgs();
Object[] args = BytesValueEncoding.decode(bytesValues, handleMethod.getParameterTypes());

retn = ReflectionUtils.invokeMethod(handleMethod, contractInstance, args);

} catch (LedgerException e) {
error = e;
} catch (Throwable e) {
String errorMessage = String.format("Error occurred while processing event[%s] of contract[%s]! --%s",
eventContext.getEvent(), address.toString(), e.getMessage());
error = new ContractExecuteException(errorMessage, e);
}

if (evtProcAwire != null) {
try {
evtProcAwire.postEvent(eventContext, error);
} catch (Throwable e) {
String errorMessage = "Error occurred while posting contract event! --" + e.getMessage();
LOGGER.error(errorMessage, e);
throw new ContractExecuteException(errorMessage);
}
}
if (error != null) {
// Rethrow error;
throw error;
}

BytesValue retnBytes = BytesValueEncoding.encodeSingle(retn, handleMethod.getReturnType());
return retnBytes;
}

protected abstract Object getContractInstance();

protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractContractCode.class);
private Bytes address;
private long version;

public AbstractContractCode(Bytes address, long version) {
this.address = address;
this.version = version;
}

@Override
public Bytes getAddress() {
return address;
}

@Override
public long getVersion() {
return version;
}

@Override
public BytesValue processEvent(ContractEventContext eventContext) {
Object retn = null;
LedgerException error = null;
Object contractInstance = null;
try {
contractInstance = getContractInstance();
// 执行预处理;
beforeEvent(contractInstance, eventContext);

// 合约方法执行
retn = doProcessEvent(contractInstance, eventContext);

} catch (LedgerException e) {
error = e;
} catch (Throwable e) {
String errorMessage = String.format("Error occurred while processing event[%s] of contract[%s]! --%s",
eventContext.getEvent(), address.toString(), e.getMessage());
error = new ContractExecuteException(errorMessage, e);
}

try {
postEvent(contractInstance, eventContext, error);
} catch (Throwable e) {
throw new ContractExecuteException("Error occurred while posting contract event!", e);
}
if (error != null) {
throw error;
}
return (BytesValue) retn;
}

protected abstract Object getContractInstance();

protected abstract void beforeEvent(Object contractInstance, ContractEventContext eventContext);

protected abstract BytesValue doProcessEvent(Object contractInstance, ContractEventContext eventContext);

protected abstract void postEvent(Object contractInstance, ContractEventContext eventContext, LedgerException error);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.jd.blockchain.contract.jvm;

import com.jd.blockchain.contract.engine.ContractCode;
import utils.Bytes;

/**
* 合约加载器
*/
public interface ContractCodeLoader {

ContractCode loadContract(Bytes address, long version, byte[] codeBytes);

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,46 @@

import com.jd.blockchain.contract.engine.ContractCode;
import com.jd.blockchain.contract.engine.ContractEngine;
import com.jd.blockchain.runtime.Module;
import com.jd.blockchain.runtime.RuntimeContext;

import com.jd.blockchain.ledger.ContractInfo;
import com.jd.blockchain.ledger.ContractLang;
import utils.Bytes;

import java.util.HashMap;
import java.util.Map;

public class JVMContractEngine implements ContractEngine {

private RuntimeContext runtimeContext = RuntimeContext.get();

private String getCodeName(Bytes address, long version) {
return address.toBase58() + "_" + version;
}

@Override
public ContractCode getContract(Bytes address, long version) {
String codeName = getCodeName(address, version);
Module module = runtimeContext.getDynamicModule(codeName);
if (module == null) {
return null;
}
return new JavaContractCode(address, version, module);
}

@Override
public ContractCode setupContract(Bytes address, long version, byte[] code) {
//is there the contractCode before setup? if yes ,then return;
ContractCode contractCode = getContract(address, version);
if(contractCode != null){
return contractCode;
private static final Map<ContractLang, ContractCodeLoader> loaders = new HashMap<>();
// TODO imuge LRU
private Map<String, ContractCode> contracts = new HashMap<>();

static {
loaders.put(ContractLang.Java, new JavaContractCodeLoader());
loaders.put(ContractLang.JavaScript, new JavaScriptContractCodeLoader());
}

private String getCodeName(Bytes address, long version) {
return address.toBase58() + "_" + version;
}

@Override
public ContractCode setupContract(ContractInfo contractInfo) {
Bytes address = contractInfo.getAddress();
long version = contractInfo.getChainCodeVersion();
String codeName = getCodeName(address, version);
ContractCode contractCode = contracts.get(version);
if (null == contractCode) {
synchronized (JVMContractEngine.class) {
contractCode = contracts.get(version);
if (null == contractCode) {
contractCode = loaders.get(contractInfo.getLang()).loadContract(address, version, contractInfo.getChainCode());
if (null != contractCode) {
contracts.put(codeName, contractCode);
}
}
}
}
String codeName = getCodeName(address, version);
Module module = runtimeContext.createDynamicModule(codeName, code);
if (module == null) {
return null;
}
return new JavaContractCode(address, version, module);
}

return contractCode;
}
}
Loading

0 comments on commit aaf58e8

Please sign in to comment.