Skip to content

Commit

Permalink
Merge branch 'smart-contract' into 'master'
Browse files Browse the repository at this point in the history
Smart contract

See merge request open-platform/chain!261
  • Loading branch information
george-bisiarin committed Nov 29, 2018
2 parents 8950f96 + 5310132 commit 442c469
Show file tree
Hide file tree
Showing 31 changed files with 425 additions and 61 deletions.
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
## Added
- Smart contract: loading contract to JVM
- Smart contract: simple contact validation

## Changed
-Core: chain synchronization improved (request for chain of blocks instead of last block only)
-Core: time synchronization improved (based on time-synchronized nodes)
- Core: chain synchronization improved (request for chain of blocks instead of last block only)
- Core: time synchronization improved (based on time-synchronized nodes)


## [1.3.0] - 2018-11-16
## Added
Expand Down
6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ dependencies {
//JAXB
compile("javax.xml.bind:jaxb-api:2.3.0")

// Byte code
compile("org.ow2.asm:asm:7.0")
compile("org.ow2.asm:asm-commons:7.0")

// Tests
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('io.projectreactor:reactor-test')
Expand Down Expand Up @@ -83,7 +87,7 @@ compileTestKotlin {
}
noArg {
annotations(
"javax.persistence.MappedSuperclass",
"javax.persistence.MappedSuperclass",
"javax.persistence.Entity",
"io.openfuture.chain.core.annotation.NoArgConstructor"
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.openfuture.chain.smartcontract.annotation
package io.openfuture.chain.smartcontract.core.annotation

@Target(AnnotationTarget.FUNCTION)
annotation class ContractConstruct
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.openfuture.chain.smartcontract.annotation
package io.openfuture.chain.smartcontract.core.annotation

@Target(AnnotationTarget.FUNCTION)
annotation class ContractMethod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package io.openfuture.chain.smartcontract.exception
package io.openfuture.chain.smartcontract.core.exception

class RequiredException(message: String?) : SmartContractException(message ?: "Required Exception")
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package io.openfuture.chain.smartcontract.exception
package io.openfuture.chain.smartcontract.core.exception

abstract class SmartContractException(message: String) : RuntimeException("Smart contract exception: $message")
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.openfuture.chain.smartcontract.model
package io.openfuture.chain.smartcontract.core.model

open class Address(private val address: String) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.openfuture.chain.smartcontract.model
package io.openfuture.chain.smartcontract.core.model

abstract class Event {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package io.openfuture.chain.smartcontract.model
package io.openfuture.chain.smartcontract.core.model

class Message(val data: ByteArray, val value: Long, val sender: Address)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.openfuture.chain.smartcontract.model
package io.openfuture.chain.smartcontract.core.model

import io.openfuture.chain.smartcontract.exception.RequiredException
import io.openfuture.chain.smartcontract.utils.AddressUtils
import io.openfuture.chain.smartcontract.core.exception.RequiredException
import io.openfuture.chain.smartcontract.core.utils.AddressUtils

abstract class SmartContract(ownerAddress: String) {

Expand All @@ -10,14 +10,6 @@ abstract class SmartContract(ownerAddress: String) {
protected var address: Address = Address(AddressUtils.generateContractAddress(ownerAddress, "0"))


protected fun transfer(address: String, amount: Long) {
transfer(Address(address), amount)
}

protected fun transfer(address: Address, amount: Long) {
TODO()
}

protected fun required(value: Boolean, message: String? = null) {
if (!value) {
throw RequiredException(message)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.test.io.openfuture.chain.smartcontract.core.service

import io.openfuture.chain.smartcontract.core.model.SmartContract

interface ContractService {

fun get(address: String): SmartContract

}

interface TransactionService {

fun transfer(senderAddress: String, recipientAddress: String, amount: Long)

}

interface BlockService {

fun blockHash(height: Long): String

fun blockTimestamp(height: Long): String

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.openfuture.chain.smartcontract.utils
package io.openfuture.chain.smartcontract.core.utils

import org.bouncycastle.pqc.math.linearalgebra.ByteUtils
import kotlin.text.Charsets.UTF_8
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.openfuture.chain.smartcontract.core.utils

import org.bouncycastle.pqc.math.linearalgebra.ByteUtils

object ByteUtils {

fun toHexString(bytes: ByteArray): String = ByteUtils.toHexString(bytes)

fun fromHexString(hex: String): ByteArray = ByteUtils.fromHexString(hex)

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.openfuture.chain.smartcontract.utils
package io.openfuture.chain.smartcontract.core.utils

import org.bouncycastle.crypto.digests.RIPEMD160Digest
import org.bouncycastle.jcajce.provider.digest.Keccak
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.openfuture.chain.smartcontract.deploy.domain

import io.openfuture.chain.smartcontract.deploy.utils.asPackagePath
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes.ASM6
import java.nio.file.Path


class ClassSource(
val bytes: ByteArray
) {

companion object {
fun isClass(path: Path): Boolean = path.fileName.toString().endsWith(".class", true)
}

val reader = ClassReader(bytes)
val writer = ClassWriter(reader, ASM6)

/**
* Fully qualified class name, e.g. io.openfuture.chain.HelloWorld
*/
val qualifiedName
get() = reader.className.asPackagePath


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.openfuture.chain.smartcontract.deploy.domain

class LoadedClass(
val clazz: Class<*>,
val byteCode: ByteArray
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.openfuture.chain.smartcontract.deploy.exception

class ClassLoadingException(
message: String? = "Class loading failed",
cause: Throwable? = null
) : RuntimeException(message, cause)
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.openfuture.chain.smartcontract.deploy.load

import io.openfuture.chain.smartcontract.deploy.domain.ClassSource.Companion.isClass
import io.openfuture.chain.smartcontract.deploy.domain.LoadedClass
import io.openfuture.chain.smartcontract.deploy.exception.ClassLoadingException
import io.openfuture.chain.smartcontract.deploy.utils.asResourcePath
import io.openfuture.chain.smartcontract.deploy.utils.toURL
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.FileNotFoundException
import java.io.IOException
import java.net.URL
import java.net.URLClassLoader
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths


class SourceClassLoader(
paths: List<Path> = emptyList()
) : URLClassLoader(resolvePaths(paths), getSystemClassLoader()) {

companion object {
private val log: Logger = LoggerFactory.getLogger(SourceClassLoader::class.java)
}

private val classes: MutableMap<String, LoadedClass> = mutableMapOf()


override fun loadClass(name: String): Class<*> = loadClass(name, false)

override fun loadClass(name: String, resolve: Boolean): Class<*> {
try {
//todo validate
return super.loadClass(name, resolve)
} catch (ex: Throwable) {
throw ClassLoadingException(ex.message, ex)
}
}

override fun findClass(name: String): Class<*> {
val loadedClass = classes[name]
if (null != loadedClass) {
log.trace("Class $name already loaded")
return loadedClass.clazz
}

val bytes = readClassBytes(name)
val clazz = loadBytes(name, bytes).clazz
classes[name] = LoadedClass(clazz, bytes)
resolveClass(clazz)
return clazz
}

fun loadBytes(className: String, bytes: ByteArray): LoadedClass {
try {
return LoadedClass(defineClass(className, bytes, 0, bytes.size), bytes)
} catch (ex: Throwable) {
throw ClassLoadingException(ex.message, ex)
}
}

private fun readClassBytes(fullyQualifiedClassName: String): ByteArray {
try {
return (getResourceAsStream("${fullyQualifiedClassName.asResourcePath}.class")
?: throw ClassLoadingException("Class not found $fullyQualifiedClassName")).readBytes()
} catch (e: IOException) {
throw ClassLoadingException("Error reading bytecode", e)
}
}

}


private val homeDirectory: Path
get() = Paths.get(System.getProperty("user.home"))


private fun resolvePaths(paths: List<Path>): Array<URL> = paths.map { expandPath(it) }.flatMap { path ->
when {
!Files.exists(path) -> throw FileNotFoundException("File not found; $path")
isClass(path) || Files.isDirectory(path) -> listOf(path.toURL())
else -> throw IllegalArgumentException("Expected a class file, but found $path")
}
}.toTypedArray()

private fun expandPath(path: Path): Path = if (path.toString().startsWith("~/")) {
homeDirectory.resolve(path.toString().removePrefix("~/"))
} else path

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.openfuture.chain.smartcontract.deploy.service

import io.openfuture.chain.smartcontract.deploy.domain.ClassSource
import io.openfuture.chain.smartcontract.deploy.load.SourceClassLoader
import org.springframework.stereotype.Service

@Service
class DefaultContractService : ContractService {

private val classLoader = SourceClassLoader()


override fun deploy(bytes: ByteArray) {
// simple deploy method
val source = ClassSource(bytes)
classLoader.loadBytes(source.qualifiedName, bytes)
}

override fun run(className: String, method: String, vararg params: Any) {
//run a method of a contract in separate thread
TODO("not implemented")
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.openfuture.chain.smartcontract.deploy.service

interface ContractService {

fun deploy(bytes: ByteArray)

fun run(className: String, method: String, vararg params: Any)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.openfuture.chain.smartcontract.deploy.utils

import java.net.URL
import java.nio.file.Path

fun Path.toURL(): URL = this.toUri().toURL()

val String.asPackagePath: String get() = this.replace('/', '.')
val String.asResourcePath: String get() = this.replace('.', '/')
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.test.io.openfuture.chain.smartcontract.deploy.validation

object BlackList {

private val entries = setOf(
"java/awt/.*",
"java/beans/.*",
"java/lang/invoke/.*",
"java/lang/.*Thread.*",
"java/lang/Shutdown.*",
"java/lang/ref/.*",
"java/lang/reflect/InvocationHandler.*",
"java/lang/reflect/Proxy.*",
"java/lang/reflect/Weak.*",
"java/io/.*File.*",
"java/net/.*Content.*",
"java/net/Host.*",
"java/net/Inet.*",
"java/nio/file/Path.*",
"java/nio/file/attribute/.*",
"java/util/SplittableRandom.*",
"java/util/Random.*",
"java/util/WeakHashMap.*",
"java/util/concurrent/.*",
"java/util/concurrent/locks/.*",
"javax/activation/.*"
).map { "[\\(\\)L]?$it".toRegex() }

fun matches(className: String): Boolean = entries.any { it.matches(className) }

}
Loading

0 comments on commit 442c469

Please sign in to comment.