From f782285d8da8ed20431cdbc035ba11ee5fe21535 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Mon, 25 Sep 2017 15:50:24 +0800 Subject: [PATCH 01/22] release smart contract version 0.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1 add avm virtual machine support 1 add smart contract API support, it can be used to interact with blockchain data 2 add smart contract storage support, it can be used to storage smart contract data 3 add deploy transaction support, it was used to deploy smart contract 3 add invoke transaction support, it was used to invoke smart contract 4 add smart contract websocket support, we can get smart contract return value, which release by zhouxi 5 add evm virtual machine support, but it is not the full version --- common/common.go | 17 + common/hash.go | 26 + common/serialization/serialize.go | 11 + common/uint160.go | 21 + core/asset/asset.go | 8 + core/code/FunctionCode.go | 41 +- core/contract/contract.go | 19 +- core/contract/contractBuilder.go | 6 +- core/contract/contractParameter.go | 5 + core/contract/program/ProgramBuilder.go | 20 +- core/ledger/block.go | 11 +- core/ledger/blockdata.go | 7 + core/ledger/header.go | 8 + core/ledger/ledgerStore.go | 4 + core/signature/signature.go | 4 +- core/store/ChainStore/CacheCodeTable.go | 24 + core/store/ChainStore/ChainStore.go | 137 ++++- core/store/ChainStore/DBCache.go | 140 +++++ core/store/DataEntryPrefix.go | 36 ++ core/transaction/TransactionBuilder.go | 45 ++ core/transaction/payload/DeployCode.go | 42 +- core/transaction/payload/InvokeCode.go | 51 ++ core/transaction/transaction.go | 23 + core/transaction/txAttribute.go | 9 + core/transaction/txOutput.go | 7 + core/transaction/utxoTxInput.go | 7 + core/validation/txValidator.go | 1 + core/validation/validation.go | 21 +- net/httpjsonrpc/TransPayloadToHex.go | 25 +- net/httprestful/common/common.go | 40 +- net/httpwebsocket/server.go | 12 + net/httpwebsocket/websocket/server.go | 2 + net/message/consensus.go | 6 + smartcontract/errors/errors.go | 9 + smartcontract/service/statemachine.go | 287 ++++++++++ smartcontract/service/statereader.go | 684 +++++++++++++++++++++++ smartcontract/service/storagecontext.go | 19 + smartcontract/service/types.go | 102 ++++ smartcontract/smartcontract.go | 235 ++++++++ smartcontract/states/accountstate.go | 62 ++ smartcontract/states/assetstate.go | 127 +++++ smartcontract/states/contractstate.go | 114 ++++ smartcontract/states/interfaces.go | 37 ++ smartcontract/states/statebase.go | 26 + smartcontract/states/states_test.go | 101 ++++ smartcontract/states/storageitem.go | 39 ++ smartcontract/states/storagekey.go | 42 ++ smartcontract/states/validatorstate.go | 30 + smartcontract/storage/dbcache.go | 58 ++ smartcontract/storage/rwset.go | 65 +++ smartcontract/types/types.go | 29 + vm/avm/ECDsaCrypto.go | 41 ++ vm/avm/common.go | 507 +++++++++++++++++ vm/avm/common_test.go | 23 + vm/avm/doc.go | 14 + vm/avm/errors/errors.go | 22 + vm/avm/execution_context.go | 64 +++ vm/avm/execution_engine.go | 352 ++++++++++++ vm/avm/func_arithmetic.go | 49 ++ vm/avm/func_arithmetic_test.go | 340 +++++++++++ vm/avm/func_array.go | 69 +++ vm/avm/func_array_test.go | 81 +++ vm/avm/func_bitwise.go | 14 + vm/avm/func_crypto.go | 55 ++ vm/avm/func_flowcontrol.go | 75 +++ vm/avm/func_pushdata.go | 30 + vm/avm/func_splice.go | 40 ++ vm/avm/func_stack.go | 101 ++++ vm/avm/func_validate.go | 257 +++++++++ vm/avm/interfaces/code_container.go | 6 + vm/avm/interfaces/crypto.go | 9 + vm/avm/interfaces/icodetable.go | 5 + vm/avm/interfaces/interop.go | 5 + vm/avm/interop_service.go | 72 +++ vm/avm/opcode.go | 124 ++++ vm/avm/opcode_exec.go | 121 ++++ vm/avm/params.go | 10 + vm/avm/params_builder.go | 76 +++ vm/avm/stack.go | 83 +++ vm/avm/stackitem.go | 21 + vm/avm/types/array.go | 56 ++ vm/avm/types/boolean.go | 52 ++ vm/avm/types/bytearray.go | 65 +++ vm/avm/types/integer.go | 51 ++ vm/avm/types/interop.go | 43 ++ vm/avm/types/stackiteminterface.go | 16 + vm/avm/utils/vm_reader.go | 111 ++++ vm/avm/utils/vmreader_test.go | 42 ++ vm/avm/vm_state.go | 12 + vm/common_test.go | 11 - vm/errors/errors.go | 11 - vm/evm/Interface.go | 20 + vm/evm/abi/abi.go | 357 ++++++++++++ vm/evm/abi/argument.go | 33 ++ vm/evm/abi/error.go | 52 ++ vm/evm/abi/event.go | 7 + vm/evm/abi/method.go | 63 +++ vm/evm/abi/number.go | 31 + vm/evm/abi/packing.go | 70 +++ vm/evm/abi/reflect.go | 42 ++ vm/evm/abi/type.go | 177 ++++++ vm/evm/analysis.go | 38 ++ vm/evm/common/common.go | 122 ++++ vm/evm/contract.go | 46 ++ vm/evm/contracts.go | 73 +++ vm/evm/crypto/crypto.go | 38 ++ vm/evm/evm_test.go | 60 ++ vm/evm/execute_engine.go | 105 ++++ vm/evm/instructions.go | 548 ++++++++++++++++++ vm/evm/instructions_test.go | 46 ++ vm/evm/jump_table.go | 671 ++++++++++++++++++++++ vm/evm/memory.go | 70 +++ vm/evm/memory_table.go | 10 + vm/evm/opcode.go | 174 ++++++ vm/evm/params.go | 5 + vm/evm/stack.go | 76 +++ vm/evm/stack_table.go | 26 + vm/evm/test_case/ballot/ballot.sol | 144 +++++ vm/evm/test_case/ballot/ballot_test.go | 93 +++ vm/evm/test_case/coin/coin.sol | 25 + vm/evm/test_case/coin/coin_test.go | 70 +++ vm/evm/test_case/deal/deal.sol | 27 + vm/evm/test_case/deal/deal_test.go | 87 +++ vm/evm/test_case/evm.go | 37 ++ vm/evm/test_case/example/example.sol | 32 ++ vm/evm/test_case/example/example_test.go | 48 ++ vm/execution_context.go | 29 - vm/execution_engine.go | 201 ------- vm/func_arithmetic.go | 90 --- vm/func_array.go | 71 --- vm/func_bitwise.go | 29 - vm/func_crypto.go | 107 ---- vm/func_flowcontrol.go | 80 --- vm/func_splice.go | 100 ---- vm/func_stack.go | 173 ------ vm/interfaces/executiontable.go | 5 - vm/interfaces/script_container.go | 5 - vm/interfaces/signableobject.go | 5 - vm/interop_service.go | 50 -- vm/opcode.go | 120 ---- vm/opcode_exec.go | 119 ---- vm/types/stackitem.go | 14 - vm/types/types_test.go | 18 - vm/utils/stack.go | 76 --- vm/utils/stack_test.go | 46 -- 145 files changed, 9466 insertions(+), 1433 deletions(-) create mode 100644 common/hash.go create mode 100644 core/store/ChainStore/CacheCodeTable.go create mode 100644 core/store/ChainStore/DBCache.go create mode 100644 core/store/DataEntryPrefix.go create mode 100644 core/transaction/payload/InvokeCode.go create mode 100644 smartcontract/errors/errors.go create mode 100644 smartcontract/service/statemachine.go create mode 100644 smartcontract/service/statereader.go create mode 100644 smartcontract/service/storagecontext.go create mode 100644 smartcontract/service/types.go create mode 100644 smartcontract/smartcontract.go create mode 100644 smartcontract/states/accountstate.go create mode 100644 smartcontract/states/assetstate.go create mode 100644 smartcontract/states/contractstate.go create mode 100644 smartcontract/states/interfaces.go create mode 100644 smartcontract/states/statebase.go create mode 100644 smartcontract/states/states_test.go create mode 100644 smartcontract/states/storageitem.go create mode 100644 smartcontract/states/storagekey.go create mode 100644 smartcontract/states/validatorstate.go create mode 100644 smartcontract/storage/dbcache.go create mode 100644 smartcontract/storage/rwset.go create mode 100644 smartcontract/types/types.go create mode 100644 vm/avm/ECDsaCrypto.go create mode 100644 vm/avm/common.go create mode 100644 vm/avm/common_test.go create mode 100644 vm/avm/doc.go create mode 100644 vm/avm/errors/errors.go create mode 100644 vm/avm/execution_context.go create mode 100644 vm/avm/execution_engine.go create mode 100644 vm/avm/func_arithmetic.go create mode 100644 vm/avm/func_arithmetic_test.go create mode 100644 vm/avm/func_array.go create mode 100644 vm/avm/func_array_test.go create mode 100644 vm/avm/func_bitwise.go create mode 100644 vm/avm/func_crypto.go create mode 100644 vm/avm/func_flowcontrol.go create mode 100644 vm/avm/func_pushdata.go create mode 100644 vm/avm/func_splice.go create mode 100644 vm/avm/func_stack.go create mode 100644 vm/avm/func_validate.go create mode 100644 vm/avm/interfaces/code_container.go create mode 100644 vm/avm/interfaces/crypto.go create mode 100644 vm/avm/interfaces/icodetable.go create mode 100644 vm/avm/interfaces/interop.go create mode 100644 vm/avm/interop_service.go create mode 100644 vm/avm/opcode.go create mode 100644 vm/avm/opcode_exec.go create mode 100644 vm/avm/params.go create mode 100644 vm/avm/params_builder.go create mode 100644 vm/avm/stack.go create mode 100644 vm/avm/stackitem.go create mode 100644 vm/avm/types/array.go create mode 100644 vm/avm/types/boolean.go create mode 100644 vm/avm/types/bytearray.go create mode 100644 vm/avm/types/integer.go create mode 100644 vm/avm/types/interop.go create mode 100644 vm/avm/types/stackiteminterface.go create mode 100644 vm/avm/utils/vm_reader.go create mode 100644 vm/avm/utils/vmreader_test.go create mode 100644 vm/avm/vm_state.go delete mode 100644 vm/common_test.go delete mode 100755 vm/errors/errors.go create mode 100644 vm/evm/Interface.go create mode 100644 vm/evm/abi/abi.go create mode 100644 vm/evm/abi/argument.go create mode 100644 vm/evm/abi/error.go create mode 100644 vm/evm/abi/event.go create mode 100644 vm/evm/abi/method.go create mode 100644 vm/evm/abi/number.go create mode 100644 vm/evm/abi/packing.go create mode 100644 vm/evm/abi/reflect.go create mode 100644 vm/evm/abi/type.go create mode 100644 vm/evm/analysis.go create mode 100644 vm/evm/common/common.go create mode 100644 vm/evm/contract.go create mode 100644 vm/evm/contracts.go create mode 100644 vm/evm/crypto/crypto.go create mode 100644 vm/evm/evm_test.go create mode 100644 vm/evm/execute_engine.go create mode 100644 vm/evm/instructions.go create mode 100644 vm/evm/instructions_test.go create mode 100644 vm/evm/jump_table.go create mode 100644 vm/evm/memory.go create mode 100644 vm/evm/memory_table.go create mode 100644 vm/evm/opcode.go create mode 100644 vm/evm/params.go create mode 100644 vm/evm/stack.go create mode 100644 vm/evm/stack_table.go create mode 100644 vm/evm/test_case/ballot/ballot.sol create mode 100644 vm/evm/test_case/ballot/ballot_test.go create mode 100644 vm/evm/test_case/coin/coin.sol create mode 100644 vm/evm/test_case/coin/coin_test.go create mode 100644 vm/evm/test_case/deal/deal.sol create mode 100644 vm/evm/test_case/deal/deal_test.go create mode 100644 vm/evm/test_case/evm.go create mode 100644 vm/evm/test_case/example/example.sol create mode 100644 vm/evm/test_case/example/example_test.go delete mode 100755 vm/execution_context.go delete mode 100755 vm/execution_engine.go delete mode 100755 vm/func_arithmetic.go delete mode 100755 vm/func_array.go delete mode 100755 vm/func_bitwise.go delete mode 100755 vm/func_crypto.go delete mode 100755 vm/func_flowcontrol.go delete mode 100755 vm/func_splice.go delete mode 100755 vm/func_stack.go delete mode 100755 vm/interfaces/executiontable.go delete mode 100644 vm/interfaces/script_container.go delete mode 100755 vm/interfaces/signableobject.go delete mode 100644 vm/interop_service.go delete mode 100755 vm/opcode.go delete mode 100644 vm/opcode_exec.go delete mode 100644 vm/types/stackitem.go delete mode 100644 vm/types/types_test.go delete mode 100644 vm/utils/stack.go delete mode 100644 vm/utils/stack_test.go diff --git a/common/common.go b/common/common.go index f1713f97..0e94904f 100644 --- a/common/common.go +++ b/common/common.go @@ -48,6 +48,14 @@ func BytesToInt16(b []byte) int16 { return int16(tmp) } +func BytesToInt(b [] byte) []int { + i := make([]int, len(b)) + for k,v := range b { + i[k] = int(v) + } + return i +} + func IsEqualBytes(b1 []byte, b2 []byte) bool { len1 := len(b1) len2 := len(b2) @@ -72,6 +80,15 @@ func HexToBytes(value string) ([]byte, error) { return hex.DecodeString(value) } +func ToArrayReverse(arr []byte) []byte { + l := len(arr) + x := make([]byte, 0) + for i := l - 1; i >= 0 ;i--{ + x = append(x, arr[i]) + } + return x +} + func BytesReverse(u []byte) []byte { for i, j := 0, len(u)-1; i < j; i, j = i+1, j-1 { u[i], u[j] = u[j], u[i] diff --git a/common/hash.go b/common/hash.go new file mode 100644 index 00000000..9781cef9 --- /dev/null +++ b/common/hash.go @@ -0,0 +1,26 @@ +package common + +import "math/big" + +const ( + HashLength = 32 +) + +type Hash [HashLength]byte + +func BytesToHash(b []byte) Hash { + var h Hash + h.SetBytes(b) + return h +} + +func (h *Hash) SetBytes(b []byte) { + if len(b) > len(h) { + b = b[len(b)-HashLength:] + } + copy(h[HashLength-len(b):], b) +} + +func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } +func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) } +func (h Hash) Bytes() []byte { return h[:] } diff --git a/common/serialization/serialize.go b/common/serialization/serialize.go index ca67d26b..07e810cc 100644 --- a/common/serialization/serialize.go +++ b/common/serialization/serialize.go @@ -288,3 +288,14 @@ func ReadBool(reader io.Reader) (bool, error) { err := binary.Read(reader, binary.LittleEndian, &x) return x, err } + +func WriteByte(writer io.Writer, val byte) error { + err := binary.Write(writer, binary.LittleEndian, val) + return err +} + +func ReadByte(reader io.Reader) (byte, error) { + var x byte + err := binary.Read(reader, binary.LittleEndian, &x) + return x, err +} \ No newline at end of file diff --git a/common/uint160.go b/common/uint160.go index 1d3d1a2d..12cc9157 100644 --- a/common/uint160.go +++ b/common/uint160.go @@ -132,3 +132,24 @@ func ToScriptHash(address string) (Uint160, error) { return ph, nil } + +func (u *Uint160) SetBytes(b []byte) { + if len(b) > len(u) { + b = b[len(b)-UINT160SIZE:] + } + copy(u[UINT160SIZE-len(b):], b) +} + +func BytesToUint160(b []byte) Uint160 { + u := new(Uint160) + u.SetBytes(b) + return *u +} + +func BigToUint160(b *big.Int) Uint160 { + return BytesToUint160(b.Bytes()) +} + +func (u *Uint160) Big() *big.Int { + return new(big.Int).SetBytes(u.ToArray()[:]) +} \ No newline at end of file diff --git a/core/asset/asset.go b/core/asset/asset.go index ef161d5b..7da3e46c 100644 --- a/core/asset/asset.go +++ b/core/asset/asset.go @@ -5,6 +5,7 @@ import ( . "DNA/errors" "errors" "io" + "bytes" ) //AssetType @@ -98,3 +99,10 @@ func (a *Asset) Deserialize(r io.Reader) error { } return nil } + + +func (a *Asset) ToArray() ([]byte) { + b := new(bytes.Buffer) + a.Serialize(b) + return b.Bytes() +} \ No newline at end of file diff --git a/core/code/FunctionCode.go b/core/code/FunctionCode.go index 385347a8..3c9cf52e 100644 --- a/core/code/FunctionCode.go +++ b/core/code/FunctionCode.go @@ -17,31 +17,41 @@ type FunctionCode struct { ParameterTypes []ContractParameterType // Contract return type list - ReturnTypes []ContractParameterType + ReturnType ContractParameterType + + codeHash Uint160 } // method of SerializableData func (fc *FunctionCode) Serialize(w io.Writer) error { - err := serialization.WriteVarBytes(w,ContractParameterTypeToByte(fc.ParameterTypes)) + err := serialization.WriteByte(w, byte(fc.ReturnType)) + if err != nil { + return err + } + err = serialization.WriteVarBytes(w, ContractParameterTypeToByte(fc.ParameterTypes)) if err != nil { return err } - err = serialization.WriteVarBytes(w,fc.Code) if err != nil { return err } - return nil } // method of SerializableData func (fc *FunctionCode) Deserialize(r io.Reader) error { - p,err := serialization.ReadVarBytes(r) + returnType, err := serialization.ReadByte(r) if err != nil { return err } - fc.ParameterTypes = ByteToContractParameterType(p) + fc.ReturnType = ContractParameterType(returnType) + + parameterTypes, err := serialization.ReadVarBytes(r) + if err != nil { + return err + } + fc.ParameterTypes = ByteToContractParameterType(parameterTypes) fc.Code,err = serialization.ReadVarBytes(r) if err != nil { @@ -65,18 +75,21 @@ func (fc *FunctionCode) GetParameterTypes() []ContractParameterType { // method of ICode // Get the list of return value -func (fc *FunctionCode) GetReturnTypes() []ContractParameterType { - return fc.ReturnTypes +func (fc *FunctionCode) GetReturnType() ContractParameterType { + return fc.ReturnType } // method of ICode // Get the hash of the smart contract func (fc *FunctionCode) CodeHash() Uint160 { - hash,err := ToCodeHash(fc.Code) - if err != nil { - log.Debug( fmt.Sprintf("[FunctionCode] ToCodeHash err=%s",err) ) - return Uint160{0} + u160 := Uint160{} + if fc.codeHash == u160 { + u160, err := ToCodeHash(fc.Code) + if err != nil { + log.Debug( fmt.Sprintf("[FunctionCode] ToCodeHash err=%s",err) ) + return u160 + } + fc.codeHash = u160 } - - return hash + return fc.codeHash } diff --git a/core/contract/contract.go b/core/contract/contract.go index 36ea038e..7d10f55b 100644 --- a/core/contract/contract.go +++ b/core/contract/contract.go @@ -1,18 +1,17 @@ package contract import ( - "bytes" - "errors" - "io" - . "DNA/common" "DNA/common/serialization" . "DNA/errors" - "DNA/vm" + "bytes" + "errors" + "io" + "DNA/vm/avm" ) //Contract address is the hash of contract program . -//which be used to control asset or indicate the smart contract address +//which be used to control asset or indicate the smart contract address �? //Contract include the program codes with parameters which can be executed on specific evnrioment type Contract struct { @@ -35,7 +34,7 @@ func (c *Contract) IsStandard() bool { if len(c.Code) != 35 { return false } - if c.Code[0] != 33 || c.Code[34] != byte(vm.CHECKSIG) { + if c.Code[0] != 33 || c.Code[34] != byte(avm.CHECKSIG) { return false } return true @@ -49,10 +48,10 @@ func (c *Contract) IsMultiSigContract() bool { if len(c.Code) < 37 { return false } - if c.Code[i] > byte(vm.PUSH16) { + if c.Code[i] > byte(avm.PUSH16) { return false } - if c.Code[i] < byte(vm.PUSH1) && c.Code[i] != 1 && c.Code[i] != 2 { + if c.Code[i] < byte(avm.PUSH1) && c.Code[i] != 1 && c.Code[i] != 2 { return false } @@ -111,7 +110,7 @@ func (c *Contract) IsMultiSigContract() bool { break } - if c.Code[i] != byte(vm.CHECKMULTISIG) { + if c.Code[i] != byte(avm.CHECKMULTISIG) { return false } i++ diff --git a/core/contract/contractBuilder.go b/core/contract/contractBuilder.go index 02c4978b..5d59beae 100644 --- a/core/contract/contractBuilder.go +++ b/core/contract/contractBuilder.go @@ -5,9 +5,9 @@ import ( pg "DNA/core/contract/program" "DNA/crypto" . "DNA/errors" - "DNA/vm" "math/big" "sort" + "DNA/vm/avm" ) //create a Single Singature contract for owner @@ -43,7 +43,7 @@ func CreateSignatureRedeemScript(pubkey *crypto.PubKey) ([]byte, error) { } sb := pg.NewProgramBuilder() sb.PushData(temp) - sb.AddOp(vm.CHECKSIG) + sb.AddOp(avm.CHECKSIG) return sb.ToArray(), nil } @@ -90,6 +90,6 @@ func CreateMultiSigRedeemScript(m int, pubkeys []*crypto.PubKey) ([]byte, error) } sb.PushNumber(big.NewInt(int64(len(pubkeys)))) - sb.AddOp(vm.CHECKMULTISIG) + sb.AddOp(avm.CHECKMULTISIG) return sb.ToArray(), nil } diff --git a/core/contract/contractParameter.go b/core/contract/contractParameter.go index d0924f78..83a62e3b 100644 --- a/core/contract/contractParameter.go +++ b/core/contract/contractParameter.go @@ -10,4 +10,9 @@ const ( Hash160 Hash256 ByteArray + PublicKey + String + Object + Array = 0x10 + Void = 0xff ) diff --git a/core/contract/program/ProgramBuilder.go b/core/contract/program/ProgramBuilder.go index 39149ebb..0afea757 100644 --- a/core/contract/program/ProgramBuilder.go +++ b/core/contract/program/ProgramBuilder.go @@ -2,9 +2,9 @@ package program import ( . "DNA/common" - "DNA/vm" "bytes" "math/big" + "DNA/vm/avm" ) type ProgramBuilder struct { @@ -13,11 +13,11 @@ type ProgramBuilder struct { func NewProgramBuilder() *ProgramBuilder { return &ProgramBuilder{ - //TODO: add sync pool for create ProgramBuilder + //TODO: add sync pool for create ProgramBuilder } } -func (pb *ProgramBuilder) AddOp(op vm.OpCode) { +func (pb *ProgramBuilder) AddOp(op avm.OpCode) { pb.buffer.WriteByte(byte(op)) } @@ -27,15 +27,15 @@ func (pb *ProgramBuilder) AddCodes(codes []byte) { func (pb *ProgramBuilder) PushNumber(number *big.Int) { if number.Cmp(big.NewInt(-1)) == 0 { - pb.AddOp(vm.PUSHM1) + pb.AddOp(avm.PUSHM1) return } if number.Cmp(big.NewInt(0)) == 0 { - pb.AddOp(vm.PUSH0) + pb.AddOp(avm.PUSH0) return } if number.Cmp(big.NewInt(0)) == 1 && number.Cmp(big.NewInt(16)) <= 0 { - pb.AddOp(vm.OpCode(byte(vm.PUSH1) - 1 + number.Bytes()[0])) + pb.AddOp(avm.OpCode(byte(avm.PUSH1) - 1 + number.Bytes()[0])) return } pb.PushData(number.Bytes()) @@ -46,20 +46,20 @@ func (pb *ProgramBuilder) PushData(data []byte) { return //TODO: add error } - if len(data) <= int(vm.PUSHBYTES75) { + if len(data) <= int(avm.PUSHBYTES75) { pb.buffer.WriteByte(byte(len(data))) pb.buffer.Write(data[0:len(data)]) } else if len(data) < 0x100 { - pb.AddOp(vm.PUSHDATA1) + pb.AddOp(avm.PUSHDATA1) pb.buffer.WriteByte(byte(len(data))) pb.buffer.Write(data[0:len(data)]) } else if len(data) < 0x10000 { - pb.AddOp(vm.PUSHDATA2) + pb.AddOp(avm.PUSHDATA2) dataByte := IntToBytes(len(data)) pb.buffer.Write(dataByte[0:2]) pb.buffer.Write(data[0:len(data)]) } else { - pb.AddOp(vm.PUSHDATA4) + pb.AddOp(avm.PUSHDATA4) dataByte := IntToBytes(len(data)) pb.buffer.Write(dataByte[0:4]) pb.buffer.Write(data[0:len(data)]) diff --git a/core/ledger/block.go b/core/ledger/block.go index 7a581439..c8661779 100644 --- a/core/ledger/block.go +++ b/core/ledger/block.go @@ -10,9 +10,10 @@ import ( "DNA/core/transaction/payload" "DNA/crypto" . "DNA/errors" - "DNA/vm" + "DNA/vm/avm" "io" "time" + "bytes" ) const BlockVersion uint32 = 0 @@ -116,6 +117,12 @@ func (b *Block) GetMessage() []byte { return sig.GetHashData(b) } +func (b *Block) ToArray() ([]byte) { + bf := new(bytes.Buffer) + b.Serialize(bf) + return bf.Bytes() +} + func (b *Block) GetProgramHashes() ([]Uint160, error) { return b.Blockdata.GetProgramHashes() @@ -164,7 +171,7 @@ func GenesisBlockInit(defaultBookKeeper []*crypto.PubKey) (*Block, error) { NextBookKeeper: nextBookKeeper, Program: &program.Program{ Code: []byte{}, - Parameter: []byte{byte(vm.PUSHT)}, + Parameter: []byte{byte(avm.PUSHT)}, }, } //transaction diff --git a/core/ledger/blockdata.go b/core/ledger/blockdata.go index 339864e9..2d23d779 100644 --- a/core/ledger/blockdata.go +++ b/core/ledger/blockdata.go @@ -9,6 +9,7 @@ import ( "crypto/sha256" "errors" "io" + "bytes" ) type Blockdata struct { @@ -157,3 +158,9 @@ func (bd *Blockdata) Hash() Uint256 { func (bd *Blockdata) GetMessage() []byte { return sig.GetHashData(bd) } + +func (bd *Blockdata) ToArray() ([]byte) { + b := new(bytes.Buffer) + bd.Serialize(b) + return b.Bytes() +} diff --git a/core/ledger/header.go b/core/ledger/header.go index bbfb2d63..5252fc94 100644 --- a/core/ledger/header.go +++ b/core/ledger/header.go @@ -2,6 +2,7 @@ package ledger import ( "io" + "bytes" ) type Header struct { @@ -27,3 +28,10 @@ func (h *Header) Deserialize(r io.Reader) error { return nil } + +func (h *Header) ToArray() ([]byte) { + b := new(bytes.Buffer) + h.Serialize(b) + return b.Bytes() +} + diff --git a/core/ledger/ledgerStore.go b/core/ledger/ledgerStore.go index fa67277c..c02dec3c 100644 --- a/core/ledger/ledgerStore.go +++ b/core/ledger/ledgerStore.go @@ -6,6 +6,7 @@ import ( . "DNA/core/asset" tx "DNA/core/transaction" "DNA/crypto" + "DNA/smartcontract/states" ) // ILedgerStore provides func with store package. @@ -27,7 +28,10 @@ type ILedgerStore interface { SaveAsset(assetid Uint256, asset *Asset) error GetAsset(hash Uint256) (*Asset, error) + GetContract(codeHash Uint160) ([]byte, error) + GetStorage(key []byte) ([]byte, error) GetAccount(programHash Uint160) (*account.AccountState, error) + GetAssetState(assetId Uint256) (*states.AssetState, error) GetCurrentBlockHash() Uint256 GetCurrentHeaderHash() Uint256 diff --git a/core/signature/signature.go b/core/signature/signature.go index 021c4b7d..5f3e89e8 100644 --- a/core/signature/signature.go +++ b/core/signature/signature.go @@ -6,7 +6,7 @@ import ( "DNA/core/contract/program" "DNA/crypto" . "DNA/errors" - "DNA/vm/interfaces" + "DNA/vm/avm/interfaces" "bytes" "crypto/sha256" "io" @@ -14,7 +14,7 @@ import ( //SignableData describe the data need be signed. type SignableData interface { - interfaces.ISignableObject + interfaces.ICodeContainer //Get the the SignableData's program hashes GetProgramHashes() ([]common.Uint160, error) diff --git a/core/store/ChainStore/CacheCodeTable.go b/core/store/ChainStore/CacheCodeTable.go new file mode 100644 index 00000000..3d54e058 --- /dev/null +++ b/core/store/ChainStore/CacheCodeTable.go @@ -0,0 +1,24 @@ +package ChainStore + +import ( + "DNA/core/store" + "DNA/smartcontract/states" +) + +type CacheCodeTable struct { + dbCache *DBCache +} + +func NewCacheCodeTable(dbCache *DBCache) *CacheCodeTable { + return &CacheCodeTable{ + dbCache: dbCache, + } +} + +func (table *CacheCodeTable) GetCode(codeHash []byte) ([]byte, error) { + value, err := table.dbCache.TryGet(store.ST_Contract, string(codeHash)) + if err != nil { + return nil, err + } + return value.(*states.ContractState).Code.Code, nil +} diff --git a/core/store/ChainStore/ChainStore.go b/core/store/ChainStore/ChainStore.go index 7fc00587..f6b8a6be 100644 --- a/core/store/ChainStore/ChainStore.go +++ b/core/store/ChainStore/ChainStore.go @@ -22,12 +22,19 @@ import ( "sort" "sync" "time" + "DNA/smartcontract/states" + "DNA/smartcontract" + "DNA/net/httpwebsocket" + "DNA/smartcontract/service" + . "DNA/net/httprestful/error" ) const ( HeaderHashListCount = 2000 CleanCacheThreshold = 2 TaskChanCap = 4 + DEPLOY_TRANSACTION = "DeployTransaction" + INVOKE_TRANSACTION = "InvokeTransaction" ) var ( @@ -375,9 +382,9 @@ func (bd *ChainStore) GetCurrentBlockHash() Uint256 { return bd.headerIndex[bd.currentBlockHeight] } -func (bd *ChainStore) GetContract(hash []byte) ([]byte, error) { - prefix := []byte{byte(DATA_Contract)} - bData, err_get := bd.st.Get(append(prefix, hash...)) +func (bd *ChainStore) GetContract(codeHash Uint160) ([]byte, error) { + prefix := []byte{byte(ST_Contract)} + bData, err_get := bd.st.Get(append(prefix, codeHash.ToArray()...)) if err_get != nil { //TODO: implement error process return nil, err_get @@ -503,6 +510,17 @@ func (bd *ChainStore) SaveAsset(assetId Uint256, asset *Asset) error { return nil } +func (bd *ChainStore) GetAssetState(assetId Uint256) (*states.AssetState, error) { + assetState := new(states.AssetState) + data, err := bd.st.Get(append([]byte{byte(ST_AssetState)}, assetId.ToArray()...)) + if err != nil { + return nil, err + } + r := bytes.NewReader(data) + assetState.Deserialize(r) + return assetState, nil +} + func (bd *ChainStore) GetAsset(hash Uint256) (*Asset, error) { log.Debug(fmt.Sprintf("GetAsset Hash: %x\n", hash)) @@ -678,6 +696,7 @@ func (bd *ChainStore) persist(b *Block) error { utxoUnspents := make(map[Uint160]map[Uint256][]*tx.UTXOUnspent) unspents := make(map[Uint256][]uint16) quantities := make(map[Uint256]Fixed64) + dbCache := NewDBCache(bd) /////////////////////////////////////////////////////////////// // Get Unspents for every tx @@ -763,21 +782,23 @@ func (bd *ChainStore) persist(b *Block) error { b.Transactions[i].TxType == tx.BookKeeper || b.Transactions[i].TxType == tx.PrivacyPayload || b.Transactions[i].TxType == tx.BookKeeping || + b.Transactions[i].TxType == tx.DeployCode || + b.Transactions[i].TxType == tx.InvokeCode || b.Transactions[i].TxType == tx.DataFile { err = bd.SaveTransaction(b.Transactions[i], b.Blockdata.Height) if err != nil { return err } } - if b.Transactions[i].TxType == tx.RegisterAsset { + txHash := b.Transactions[i].Hash() + switch b.Transactions[i].TxType { + case tx.RegisterAsset: ar := b.Transactions[i].Payload.(*payload.RegisterAsset) err = bd.SaveAsset(b.Transactions[i].Hash(), ar.Asset) if err != nil { return err } - } - - if b.Transactions[i].TxType == tx.IssueAsset { + case tx.IssueAsset: results := b.Transactions[i].GetMergedAssetIDValueFromOutputs() for assetId, value := range results { if _, ok := quantities[assetId]; !ok { @@ -786,8 +807,98 @@ func (bd *ChainStore) persist(b *Block) error { quantities[assetId] = value } } - } + case tx.DeployCode: + deployCode := b.Transactions[i].Payload.(*payload.DeployCode) + codeHash := deployCode.Code.CodeHash() + dbCache.GetOrAdd(ST_Contract, string(codeHash.ToArray()), &states.ContractState{ + Code: deployCode.Code, + Name: deployCode.Name, + Version: deployCode.CodeVersion, + Author: deployCode.Author, + Email: deployCode.Email, + Description: deployCode.Description, + Language: deployCode.Language, + ProgramHash: deployCode.ProgramHash, + }) + + smartContract, err := smartcontract.NewSmartContract(&smartcontract.Context{ + Language: deployCode.Language, + Caller: deployCode.ProgramHash, + StateMachine: service.NewStateMachine(dbCache, NewDBCache(bd)), + DBCache: dbCache, + Code: deployCode.Code.Code, + Time: big.NewInt(int64(b.Blockdata.Timestamp)), + BlockNumber: big.NewInt(int64(b.Blockdata.Height)), + Gas: Fixed64(0), + }) + + if err != nil { + httpwebsocket.PushResult(txHash, SMARTCODE_ERROR, DEPLOY_TRANSACTION, err) + return err + } + + ret, err := smartContract.DeployContract() + if err != nil { + httpwebsocket.PushResult(txHash, SMARTCODE_ERROR, DEPLOY_TRANSACTION, err) + continue + } + + hash, err := ToCodeHash(ret) + if err != nil { + httpwebsocket.PushResult(txHash, SMARTCODE_ERROR, DEPLOY_TRANSACTION, err) + return err + } + httpwebsocket.PushResult(txHash, 0, DEPLOY_TRANSACTION, ToHexString(hash.ToArrayReverse())) + err = dbCache.Commit() + if err != nil { + return err + } + case tx.InvokeCode: + invokeCode := b.Transactions[i].Payload.(*payload.InvokeCode) + contract, err := bd.GetContract(invokeCode.CodeHash) + if err != nil { + log.Error("db getcontract err:", err) + httpwebsocket.PushResult(txHash, SMARTCODE_ERROR, INVOKE_TRANSACTION, err) + continue + } + state, err := states.GetStateValue(ST_Contract, contract) + if err != nil { + log.Error("states GetStateValue err:", err) + httpwebsocket.PushResult(txHash, SMARTCODE_ERROR, INVOKE_TRANSACTION, err) + return err + } + contractState := state.(*states.ContractState) + stateMachine := service.NewStateMachine(dbCache, NewDBCache(bd)) + smartContract, err := smartcontract.NewSmartContract(&smartcontract.Context{ + Language: contractState.Language, + Caller: invokeCode.ProgramHash, + StateMachine: stateMachine, + DBCache: dbCache, + CodeHash: invokeCode.CodeHash, + Input: invokeCode.Code, + SignableData: b.Transactions[i], + CacheCodeTable: NewCacheCodeTable(dbCache), + Time: big.NewInt(int64(b.Blockdata.Timestamp)), + BlockNumber: big.NewInt(int64(b.Blockdata.Height)), + Gas: Fixed64(0), + ReturnType: contractState.Code.ReturnType, + ParameterTypes: contractState.Code.ParameterTypes, + }) + if err != nil { + log.Error("smartcontract NewSmartContract err:", err) + httpwebsocket.PushResult(txHash, SMARTCODE_ERROR, INVOKE_TRANSACTION, err) + continue + } + ret, err := smartContract.InvokeContract() + if err != nil { + log.Error("smartContract InvokeContract err:", err) + httpwebsocket.PushResult(txHash, SMARTCODE_ERROR, INVOKE_TRANSACTION, err) + continue + } + stateMachine.CloneCache.Commit() + httpwebsocket.PushResult(txHash, 0, INVOKE_TRANSACTION, ret) + } for index := 0; index < len(b.Transactions[i].Outputs); index++ { output := b.Transactions[i].Outputs[index] programHash := output.ProgramHash @@ -1451,3 +1562,13 @@ func (bd *ChainStore) GetAssets() map[Uint256]*Asset { return assets } + +func (bd *ChainStore) GetStorage(key []byte) ([]byte, error) { + prefix := []byte{byte(ST_Storage)} + bData, err_get := bd.st.Get(append(prefix, key...)) + + if err_get != nil { + return nil, err_get + } + return bData, nil +} diff --git a/core/store/ChainStore/DBCache.go b/core/store/ChainStore/DBCache.go new file mode 100644 index 00000000..f4229177 --- /dev/null +++ b/core/store/ChainStore/DBCache.go @@ -0,0 +1,140 @@ +package ChainStore + +import ( + "DNA/smartcontract/storage" + "bytes" + "DNA/smartcontract/states" + "DNA/core/store" + "math/big" + "DNA/common" +) + +type DBCache struct { + RWSet *storage.RWSet + db *ChainStore +} + +func NewDBCache(db *ChainStore) *DBCache { + return &DBCache{ + RWSet: storage.NewRWSet(), + db: db, + } +} + +func (cache *DBCache) Commit() (err error) { + rwSet := cache.RWSet.WriteSet + for k, v := range rwSet { + key := make([]byte, 0) + key = append([]byte{byte(v.Prefix)}, []byte(k)...) + if v.IsDeleted { + err = cache.db.st.BatchDelete(key) + } else { + b := new(bytes.Buffer) + v.Item.Serialize(b) + value := make([]byte, 0) + value = append(value, b.Bytes()...) + err = cache.db.st.BatchPut(key, value) + } + } + return +} + +func (cache *DBCache) TryGetInternal(prefix store.DataEntryPrefix, key string) (states.IStateValueInterface, error) { + value, err := cache.db.st.Get(append([]byte{byte(prefix)}, []byte(key)...)) + if err != nil { + return nil, err + } + return states.GetStateValue(prefix, value) +} + +func (cache *DBCache) GetOrAdd(prefix store.DataEntryPrefix, key string, value states.IStateValueInterface) (states.IStateValueInterface, error) { + if v, ok := cache.RWSet.WriteSet[key]; ok { + if v.IsDeleted { + v.Item = value + v.IsDeleted = false + } + } else { + item, err := cache.TryGetInternal(prefix, key) + if err != nil && err.Error() != ErrDBNotFound.Error() { + return nil, err + } + write := &storage.Write{ + Prefix: prefix, + Item: item, + Key: key, + IsDeleted: false, + } + if write.Item == nil { + write.Item = value + + } + cache.RWSet.WriteSet[key] = write + } + return cache.RWSet.WriteSet[key].Item, nil +} + +func (cache *DBCache) TryGet(prefix store.DataEntryPrefix, key string) (states.IStateValueInterface, error) { + if v, ok := cache.RWSet.WriteSet[key]; ok { + return v.Item, nil + } else { + return cache.TryGetInternal(prefix, key) + } +} + +func (cache *DBCache) GetWriteSet() *storage.RWSet { + return cache.RWSet +} + +func (cache *DBCache) GetState(codeHash common.Uint160, loc common.Hash) (common.Hash, error) { + key := states.NewStorageKey(&codeHash, loc.Bytes()) + keyStr := storage.KeyToStr(key) + value, err := cache.TryGet(store.ST_Storage, keyStr) + if err != nil { + return common.Hash{}, err + } + return common.BytesToHash(value.(*states.StorageItem).Value), nil +} + +func (cache *DBCache) SetState(codeHash common.Uint160, loc, value common.Hash) { + key := states.NewStorageKey(&codeHash, loc.Bytes()) + item := states.NewStorageItem(value.Bytes()) + keyStr := storage.KeyToStr(key) + cache.RWSet.Add(store.ST_Storage, keyStr, item) +} + +func (cache *DBCache) GetCode(codeHash common.Uint160) ([]byte, error) { + skey := storage.KeyToStr(&codeHash) + value, err := cache.TryGet(store.ST_Storage, skey) + if err != nil { + return nil, err + } + return value.(*states.StorageItem).Value, nil +} + +func (cache *DBCache) SetCode(codeHash common.Uint160, code []byte) { + item := states.NewStorageItem(code) + skey := storage.KeyToStr(&codeHash) + cache.RWSet.Add(store.ST_Storage, skey, item) +} + +func (cache *DBCache) GetBalance(common.Uint160) *big.Int { + return big.NewInt(100) +} + +func (cache *DBCache) GetCodeSize(common.Uint160) int { + return 0 +} + +func (cache *DBCache) AddBalance(common.Uint160, *big.Int) { +} + +func (cache *DBCache) Suicide(codeHash common.Uint160) bool { + skey := storage.KeyToStr(&codeHash) + cache.RWSet.Delete(skey) + return true +} + + + + + diff --git a/core/store/DataEntryPrefix.go b/core/store/DataEntryPrefix.go new file mode 100644 index 00000000..0da4e5db --- /dev/null +++ b/core/store/DataEntryPrefix.go @@ -0,0 +1,36 @@ +package store + +// DataEntryPrefix +type DataEntryPrefix byte + +const ( + // DATA + DATA_BlockHash DataEntryPrefix = 0x00 + DATA_Header DataEntryPrefix = 0x01 + DATA_Transaction DataEntryPrefix = 0x02 + DATA_Contract DataEntryPrefix = 0x03 + + // INDEX + IX_HeaderHashList DataEntryPrefix = 0x80 + IX_Enrollment DataEntryPrefix = 0x84 + IX_Unspent DataEntryPrefix = 0x90 + IX_Unspent_UTXO DataEntryPrefix = 0x91 + IX_Vote DataEntryPrefix = 0x94 + + // ASSET + ST_Info DataEntryPrefix = 0xc0 + ST_QuantityIssued DataEntryPrefix = 0xc1 + ST_Contract DataEntryPrefix = 0xc2 + ST_Storage DataEntryPrefix = 0xc3 + ST_ACCOUNT DataEntryPrefix = 0xc4 + ST_AssetState DataEntryPrefix = 0xc5 + ST_Validator DataEntryPrefix = 0xc6 + + //SYSTEM + SYS_CurrentBlock DataEntryPrefix = 0x40 + SYS_CurrentHeader DataEntryPrefix = 0x41 + SYS_CurrentBookKeeper DataEntryPrefix = 0x42 + + //CONFIG + CFG_Version DataEntryPrefix = 0xf0 +) diff --git a/core/transaction/TransactionBuilder.go b/core/transaction/TransactionBuilder.go index a181e850..2b831dda 100644 --- a/core/transaction/TransactionBuilder.go +++ b/core/transaction/TransactionBuilder.go @@ -6,6 +6,8 @@ import ( "DNA/core/contract/program" "DNA/core/transaction/payload" "DNA/crypto" + "DNA/core/code" + "DNA/smartcontract/types" ) //initial a new transaction with asset registration payload @@ -143,3 +145,46 @@ func NewDataFileTransaction(path string, fileName string, note string, issuer *c Programs: []*program.Program{}, }, nil } + +//initial a new transaction with publish payload +func NewDeployTransaction(fc *code.FunctionCode, programHash common.Uint160, name, codeversion, author, email, desp string, language types.LangType) (*Transaction, error) { + //TODO: check arguments + DeployCodePayload := &payload.DeployCode{ + Code: fc, + Name: name, + CodeVersion: codeversion, + Author: author, + Email: email, + Description: desp, + Language: language, + ProgramHash: programHash, + } + + return &Transaction{ + TxType: DeployCode, + Payload: DeployCodePayload, + Attributes: []*TxAttribute{}, + UTXOInputs: []*UTXOTxInput{}, + BalanceInputs: []*BalanceTxInput{}, + Programs: []*program.Program{}, + }, nil +} + +//initial a new transaction with invoke payload +func NewInvokeTransaction(fc []byte, codeHash common.Uint160) (*Transaction, error) { + //TODO: check arguments + InvokeCodePayload := &payload.InvokeCode{ + Code: fc, + CodeHash: codeHash, + } + + return &Transaction{ + TxType: InvokeCode, + Payload: InvokeCodePayload, + Attributes: []*TxAttribute{}, + UTXOInputs: []*UTXOTxInput{}, + BalanceInputs: []*BalanceTxInput{}, + Programs: []*program.Program{}, + }, nil +} + diff --git a/core/transaction/payload/DeployCode.go b/core/transaction/payload/DeployCode.go index 09ca6461..1f0377f0 100644 --- a/core/transaction/payload/DeployCode.go +++ b/core/transaction/payload/DeployCode.go @@ -1,20 +1,23 @@ package payload import ( - "DNA/common/serialization" . "DNA/core/code" + "DNA/common/serialization" "io" + "DNA/smartcontract/types" + "DNA/common" ) -const DeployCodePayloadVersion byte = 0x00 - type DeployCode struct { Code *FunctionCode + Params []byte Name string CodeVersion string Author string Email string Description string + Language types.LangType + ProgramHash common.Uint160 } func (dc *DeployCode) Data(version byte) []byte { @@ -30,6 +33,12 @@ func (dc *DeployCode) Serialize(w io.Writer, version byte) error { return err } + err = serialization.WriteVarBytes(w, dc.Params) + + if err != nil { + return err + } + err = serialization.WriteVarString(w, dc.Name) if err != nil { return err @@ -55,15 +64,30 @@ func (dc *DeployCode) Serialize(w io.Writer, version byte) error { return err } + err = serialization.WriteByte(w, byte(dc.Language)) + if err != nil { + return err + } + + _, err = dc.ProgramHash.Serialize(w) + if err != nil { + return err + } return nil } func (dc *DeployCode) Deserialize(r io.Reader, version byte) error { + dc.Code = new(FunctionCode) err := dc.Code.Deserialize(r) if err != nil { return err } + dc.Params, err = serialization.ReadVarBytes(r) + if err != nil { + return err + } + dc.Name, err = serialization.ReadVarString(r) if err != nil { return err @@ -89,5 +113,17 @@ func (dc *DeployCode) Deserialize(r io.Reader, version byte) error { return err } + l, err := serialization.ReadByte(r) + if err != nil { + return err + } + dc.Language = types.LangType(l) + u := new(common.Uint160) + err = u.Deserialize(r) + if err != nil { + return err + } + dc.ProgramHash = *u return nil } + diff --git a/core/transaction/payload/InvokeCode.go b/core/transaction/payload/InvokeCode.go new file mode 100644 index 00000000..361b244c --- /dev/null +++ b/core/transaction/payload/InvokeCode.go @@ -0,0 +1,51 @@ +package payload + +import ( + "io" + "DNA/common/serialization" + "DNA/common" +) + +type InvokeCode struct { + CodeHash common.Uint160 + Code []byte + ProgramHash common.Uint160 +} + +func (ic *InvokeCode) Data(version byte) []byte { + return []byte{0} +} + +func (ic *InvokeCode) Serialize(w io.Writer, version byte) error { + ic.CodeHash.Serialize(w) + err := serialization.WriteVarBytes(w, ic.Code) + if err != nil { + return err + } + _, err = ic.ProgramHash.Serialize(w) + if err != nil { + return err + } + return nil +} + +func (ic *InvokeCode) Deserialize(r io.Reader, version byte) error { + u := new(common.Uint160) + if err := u.Deserialize(r); err != nil { + return err + } + ic.CodeHash = *u + code, err := serialization.ReadVarBytes(r) + if err != nil { + return err + } + ic.Code = code + + p := new(common.Uint160) + err = u.Deserialize(r) + if err != nil { + return err + } + ic.ProgramHash = *p + return nil +} \ No newline at end of file diff --git a/core/transaction/transaction.go b/core/transaction/transaction.go index 2e3db2cc..d59457b4 100644 --- a/core/transaction/transaction.go +++ b/core/transaction/transaction.go @@ -13,6 +13,8 @@ import ( "fmt" "io" "sort" + "bytes" + "DNA/common/log" ) //for different transaction types with different payload format @@ -28,6 +30,7 @@ const ( TransferAsset TransactionType = 0x80 Record TransactionType = 0x81 DeployCode TransactionType = 0xd0 + InvokeCode TransactionType = 0xd1 DataFile TransactionType = 0x12 ) @@ -141,6 +144,7 @@ func (tx *Transaction) Deserialize(r io.Reader) error { // tx deserialize err := tx.DeserializeUnsigned(r) if err != nil { + log.Error("Deserialize DeserializeUnsigned:", err) return NewDetailErr(err, ErrNoCode, "transaction Deserialize error") } @@ -166,6 +170,7 @@ func (tx *Transaction) DeserializeUnsigned(r io.Reader) error { var txType [1]byte _, err := io.ReadFull(r, txType[:]) if err != nil { + log.Error("DeserializeUnsigned ReadFull:", err) return err } tx.TxType = TransactionType(txType[0]) @@ -177,6 +182,7 @@ func (tx *Transaction) DeserializeUnsignedWithoutType(r io.Reader) error { _, err := io.ReadFull(r, payloadVersion[:]) tx.PayloadVersion = payloadVersion[0] if err != nil { + log.Error("DeserializeUnsignedWithoutType:", err) return err } @@ -197,6 +203,10 @@ func (tx *Transaction) DeserializeUnsignedWithoutType(r io.Reader) error { tx.Payload = new(payload.BookKeeper) case PrivacyPayload: tx.Payload = new(payload.PrivacyPayload) + case DeployCode: + tx.Payload = new(payload.DeployCode) + case InvokeCode: + tx.Payload = new(payload.InvokeCode) case DataFile: tx.Payload = new(payload.DataFile) default: @@ -204,11 +214,13 @@ func (tx *Transaction) DeserializeUnsignedWithoutType(r io.Reader) error { } err = tx.Payload.Deserialize(r, tx.PayloadVersion) if err != nil { + log.Error("tx Payload Deserialize:", err) return NewDetailErr(err, ErrNoCode, "Payload Parse error") } //attributes Len, err := serialization.ReadVarUint(r, 0) if err != nil { + log.Error("tx attributes Deserialize:", err) return err } if Len > uint64(0) { @@ -224,6 +236,8 @@ func (tx *Transaction) DeserializeUnsignedWithoutType(r io.Reader) error { //UTXOInputs Len, err = serialization.ReadVarUint(r, 0) if err != nil { + log.Error("tx UTXOInputs Deserialize:", err) + return err } if Len > uint64(0) { @@ -325,6 +339,8 @@ func (tx *Transaction) GetProgramHashes() ([]Uint160, error) { hashs = append(hashs, astHash) case TransferAsset: case Record: + case DeployCode: + case InvokeCode: case BookKeeper: issuer := tx.Payload.(*payload.BookKeeper).Issuer signatureRedeemScript, err := contract.CreateSignatureRedeemScript(issuer) @@ -385,6 +401,13 @@ func (tx *Transaction) GetMessage() []byte { return sig.GetHashData(tx) } +func (tx *Transaction) ToArray() ([]byte) { + b := new(bytes.Buffer) + tx.Serialize(b) + return b.Bytes() +} + + func (tx *Transaction) Hash() Uint256 { if tx.hash == nil { d := sig.GetHashData(tx) diff --git a/core/transaction/txAttribute.go b/core/transaction/txAttribute.go index 4c5f735c..dea2f7f1 100644 --- a/core/transaction/txAttribute.go +++ b/core/transaction/txAttribute.go @@ -5,6 +5,7 @@ import ( . "DNA/errors" "errors" "io" + "bytes" ) type TransactionAttributeUsage byte @@ -69,3 +70,11 @@ func (tx *TxAttribute) Deserialize(r io.Reader) error { return nil } + + +func (tx *TxAttribute) ToArray() ([]byte) { + b := new(bytes.Buffer) + tx.Serialize(b) + return b.Bytes() +} + diff --git a/core/transaction/txOutput.go b/core/transaction/txOutput.go index 26462005..b34d7d4e 100644 --- a/core/transaction/txOutput.go +++ b/core/transaction/txOutput.go @@ -3,6 +3,7 @@ package transaction import ( "DNA/common" "io" + "bytes" ) type TxOutput struct { @@ -22,3 +23,9 @@ func (o *TxOutput) Deserialize(r io.Reader) { o.Value.Deserialize(r) o.ProgramHash.Deserialize(r) } + +func (ui *TxOutput) ToArray() ([]byte) { + b := new(bytes.Buffer) + ui.Serialize(b) + return b.Bytes() +} \ No newline at end of file diff --git a/core/transaction/utxoTxInput.go b/core/transaction/utxoTxInput.go index 158a09f5..ec5d2446 100644 --- a/core/transaction/utxoTxInput.go +++ b/core/transaction/utxoTxInput.go @@ -5,6 +5,7 @@ import ( "DNA/common/serialization" "fmt" "io" + "bytes" ) type UTXOTxInput struct { @@ -38,6 +39,12 @@ func (ui *UTXOTxInput) Deserialize(r io.Reader) error { return nil } +func (ui *UTXOTxInput) ToArray() ([]byte) { + b := new(bytes.Buffer) + ui.Serialize(b) + return b.Bytes() +} + func (ui *UTXOTxInput) ToString() string { return fmt.Sprintf("%x%x", ui.ReferTxID.ToString(), ui.ReferTxOutputIndex) } diff --git a/core/validation/txValidator.go b/core/validation/txValidator.go index 24603dfd..5b8997b7 100644 --- a/core/validation/txValidator.go +++ b/core/validation/txValidator.go @@ -276,6 +276,7 @@ func CheckTransactionPayload(Tx *tx.Transaction) error { case *payload.PrivacyPayload: case *payload.Record: case *payload.DeployCode: + case *payload.InvokeCode: case *payload.DataFile: default: return errors.New("[txValidator],invalidate transaction payload type.") diff --git a/core/validation/validation.go b/core/validation/validation.go index 9a97424a..0dc4f123 100644 --- a/core/validation/validation.go +++ b/core/validation/validation.go @@ -5,9 +5,10 @@ import ( sig "DNA/core/signature" "DNA/crypto" . "DNA/errors" - "DNA/vm" - "DNA/vm/interfaces" + "DNA/vm/avm" "errors" + "DNA/vm/avm/interfaces" + "DNA/common" ) func VerifySignableData(signableData sig.SignableData) (bool, error) { @@ -31,13 +32,17 @@ func VerifySignableData(signableData sig.SignableData) (bool, error) { } //execute program on VM var cryptos interfaces.ICrypto - cryptos = new(vm.ECDsaCrypto) - se := vm.NewExecutionEngine(signableData, cryptos, 1200, nil, nil) - se.LoadScript(programs[i].Code, false) - se.LoadScript(programs[i].Parameter, true) - se.Execute() + cryptos = new(avm.ECDsaCrypto) + se := avm.NewExecutionEngine(signableData, cryptos, nil, nil, common.Fixed64(0)) + se.LoadCode(programs[i].Code, false) + se.LoadCode(programs[i].Parameter, true) + err := se.Execute() - if se.GetState() != vm.HALT { + if err != nil { + return false, NewDetailErr(err, ErrNoCode, "") + } + + if se.GetState() != avm.HALT { return false, NewDetailErr(errors.New("[VM] Finish State not equal to HALT."), ErrNoCode, "") } diff --git a/net/httpjsonrpc/TransPayloadToHex.go b/net/httpjsonrpc/TransPayloadToHex.go index 92345f29..80f7c2a2 100644 --- a/net/httpjsonrpc/TransPayloadToHex.go +++ b/net/httpjsonrpc/TransPayloadToHex.go @@ -3,7 +3,6 @@ package httpjsonrpc import ( . "DNA/common" "DNA/core/asset" - . "DNA/core/contract" . "DNA/core/transaction" "DNA/core/transaction/payload" "bytes" @@ -20,17 +19,20 @@ type BookKeepingInfo struct { //implement PayloadInfo define DeployCodeInfo type FunctionCodeInfo struct { Code string - ParameterTypes string - ReturnTypes string + ParameterTypes []int + ReturnType int + CodeHash string } type DeployCodeInfo struct { Code *FunctionCodeInfo Name string - CodeVersion string + Version string Author string Email string Description string + Language int + ProgramHash string } //implement PayloadInfo define IssueAssetInfo @@ -104,14 +106,23 @@ func TransPayloadToHex(p Payload) PayloadInfo { case *payload.TransferAsset: case *payload.DeployCode: obj := new(DeployCodeInfo) + obj.Code = new(FunctionCodeInfo) obj.Code.Code = ToHexString(object.Code.Code) - obj.Code.ParameterTypes = ToHexString(ContractParameterTypeToByte(object.Code.ParameterTypes)) - obj.Code.ReturnTypes = ToHexString(ContractParameterTypeToByte(object.Code.ReturnTypes)) + var params []int + for _, v := range object.Code.ParameterTypes { + params = append(params, int(v)) + } + obj.Code.ParameterTypes = params + obj.Code.ReturnType = int(object.Code.ReturnType) + codeHash := object.Code.CodeHash() + obj.Code.CodeHash = ToHexString(codeHash.ToArrayReverse()) obj.Name = object.Name - obj.CodeVersion = object.CodeVersion + obj.Version = object.CodeVersion obj.Author = object.Author obj.Email = object.Email obj.Description = object.Description + obj.Language = int(object.Language) + obj.ProgramHash = ToHexString(object.ProgramHash.ToArrayReverse()) return obj case *payload.RegisterAsset: obj := new(RegisterAssetInfo) diff --git a/net/httprestful/common/common.go b/net/httprestful/common/common.go index aea4c6d2..fad26c3b 100644 --- a/net/httprestful/common/common.go +++ b/net/httprestful/common/common.go @@ -12,6 +12,7 @@ import ( "fmt" "math" "strconv" + "DNA/smartcontract/states" ) var node Noder @@ -514,11 +515,38 @@ func GetContract(cmd map[string]interface{}) map[string]interface{} { return resp } //TODO GetContract from store - //contract, err := ledger.DefaultLedger.Store.GetContract(hash) - //if err != nil { - // resp["Error"] = Err.INVALID_PARAMS - // return resp - //} - //resp["Result"] = string(contract) + contract, err := ledger.DefaultLedger.Store.GetContract(hash) + if err != nil { + resp["Error"] = Err.INVALID_PARAMS + return resp + } + c := new(states.ContractState) + b := bytes.NewBuffer(contract) + c.Deserialize(b) + var params []int + for _, v := range c.Code.ParameterTypes { + params = append(params, int(v)) + } + codehash := c.Code.CodeHash() + funcCode := &FunctionCodeInfo{ + Code: ToHexString(c.Code.Code), + ParameterTypes: params, + ReturnType: int(c.Code.ReturnType), + CodeHash: ToHexString(codehash.ToArrayReverse()), + } + programHash := c.ProgramHash + result := DeployCodeInfo{ + Name: c.Name, + Author: c.Author, + Email: c.Email, + Version: c.Version, + Description: c.Description, + Language: int(c.Language), + Code: new(FunctionCodeInfo), + ProgramHash: ToHexString(programHash.ToArrayReverse()), + } + + result.Code = funcCode + resp["Result"] = result return resp } diff --git a/net/httpwebsocket/server.go b/net/httpwebsocket/server.go index 62c1732d..7f021fe2 100644 --- a/net/httpwebsocket/server.go +++ b/net/httpwebsocket/server.go @@ -77,6 +77,18 @@ func SetTxHashMap(txhash string, sessionid string) { } ws.SetTxHashMap(txhash, sessionid) } + +func PushResult(txHash Uint256, errcode int64, action string, result interface{}) { + if ws != nil { + resp := common.ResponsePack(Err.SUCCESS) + resp["Result"] = result + resp["Error"] = errcode + resp["Action"] = action + resp["Desc"] = Err.ErrMap[resp["Error"].(int64)] + ws.PushTxResult(ToHexString(txHash.ToArrayReverse()), resp) + } +} + func PushSmartCodeInvokeResult(txHash Uint256, errcode int64, result interface{}) { if ws == nil { return diff --git a/net/httpwebsocket/websocket/server.go b/net/httpwebsocket/websocket/server.go index 484ccc45..9a6402e1 100644 --- a/net/httpwebsocket/websocket/server.go +++ b/net/httpwebsocket/websocket/server.go @@ -131,6 +131,7 @@ func (ws *WsServer) registryMethod() { "getblockbyhash": {handler: GetBlockByHash}, "getblockheight": {handler: GetBlockHeight}, "gettransaction": {handler: GetTransactionByHash}, + "getcontract": {handler: GetContract}, "getasset": {handler: GetAssetByHash}, "getunspendoutput": {handler: GetUnspendOutput}, @@ -330,6 +331,7 @@ func (ws *WsServer) PushTxResult(txHashStr string, resp map[string]interface{}) if len(sSessionId) > 0 { ws.response(sSessionId, resp) } + ws.PushResult(resp) } func (ws *WsServer) PushResult(resp map[string]interface{}) { resp["Desc"] = Err.ErrMap[resp["Error"].(int64)] diff --git a/net/message/consensus.go b/net/message/consensus.go index e1f24bfa..b87e0a73 100644 --- a/net/message/consensus.go +++ b/net/message/consensus.go @@ -104,6 +104,12 @@ func (cp *ConsensusPayload) GetMessage() []byte { //return []byte{} } +func (b *ConsensusPayload) ToArray() ([]byte) { + bf := new(bytes.Buffer) + b.Serialize(bf) + return bf.Bytes() +} + func (msg consensus) Handle(node Noder) error { log.Debug() node.LocalNode().GetEvent("consensus").Notify(events.EventNewInventory, &msg.cons) diff --git a/smartcontract/errors/errors.go b/smartcontract/errors/errors.go new file mode 100644 index 00000000..63c0a84b --- /dev/null +++ b/smartcontract/errors/errors.go @@ -0,0 +1,9 @@ +package errors + +import "errors" + +var ( + ErrAssetNameInvalid = errors.New("asset name invalid") + ErrAssetPrecisionInvalid = errors.New("asset precision invalid") + ErrAssetAmountInvalid = errors.New("asset amount invalid") +) diff --git a/smartcontract/service/statemachine.go b/smartcontract/service/statemachine.go new file mode 100644 index 00000000..8dd89626 --- /dev/null +++ b/smartcontract/service/statemachine.go @@ -0,0 +1,287 @@ +package service + +import ( + "DNA/common" + "DNA/core/asset" + "DNA/core/code" + "DNA/core/contract" + "DNA/core/ledger" + "DNA/core/store" + "DNA/core/transaction" + "DNA/crypto" + "DNA/errors" + . "DNA/smartcontract/errors" + "DNA/smartcontract/states" + "DNA/smartcontract/storage" + "DNA/vm/avm" + "bytes" + "encoding/hex" + "fmt" + "math" +) + +type StateMachine struct { + *StateReader + CloneCache *storage.CloneCache +} + +func NewStateMachine(dbCache storage.DBCache, innerCache storage.DBCache) *StateMachine { + var stateMachine StateMachine + stateMachine.CloneCache = storage.NewCloneDBCache(innerCache, dbCache) + stateMachine.StateReader = NewStateReader() + stateMachine.StateReader.Register("Neo.Validator.Register", stateMachine.RegisterValidator) + stateMachine.StateReader.Register("Neo.Asset.Create", stateMachine.CreateAsset) + stateMachine.StateReader.Register("Neo.Contract.Create", stateMachine.CreateContract) + stateMachine.StateReader.Register("Neo.Blockchain.GetContract", stateMachine.GetContract) + stateMachine.StateReader.Register("Neo.Asset.Renew", stateMachine.AssetRenew) + stateMachine.StateReader.Register("Neo.Storage.Get", stateMachine.StorageGet) + stateMachine.StateReader.Register("Neo.Contract.Destroy", stateMachine.ContractDestory) + stateMachine.StateReader.Register("Neo.Storage.Put", stateMachine.StoragePut) + stateMachine.StateReader.Register("Neo.Storage.Delete", stateMachine.StorageDelete) + stateMachine.StateReader.Register("Neo.Contract.GetStorageContext", stateMachine.GetStorageContext) + return &stateMachine +} + +func (s *StateMachine) RegisterValidator(engine *avm.ExecutionEngine) (bool, error) { + pubkeyByte := avm.PopByteArray(engine) + pubkey, err := crypto.DecodePoint(pubkeyByte) + if err != nil { + return false, err + } + if result, err := s.StateReader.CheckWitnessPublicKey(engine, pubkey); !result { + return result, err + } + b := new(bytes.Buffer) + pubkey.Serialize(b) + validatorState, err := s.CloneCache.GetInnerCache().GetOrAdd(store.ST_Validator, b.String(), &states.ValidatorState{PublicKey: pubkey}) + if err != nil { + return false, err + } + avm.PushData(engine, validatorState) + return true, nil +} + +func (s *StateMachine) CreateAsset(engine *avm.ExecutionEngine) (bool, error) { + tx := engine.GetCodeContainer().(*transaction.Transaction) + assetId := tx.Hash() + assertType := asset.AssetType(avm.PopInt(engine)) + name := avm.PopByteArray(engine) + if len(name) > 1024 { + return false, ErrAssetNameInvalid + } + amount := avm.PopBigInt(engine) + if amount.Int64() == 0 { + return false, ErrAssetAmountInvalid + } + precision := avm.PopBigInt(engine) + if precision.Int64() > 8 { + return false, ErrAssetPrecisionInvalid + } + if amount.Int64()%int64(math.Pow(10, 8-float64(precision.Int64()))) != 0 { + return false, ErrAssetAmountInvalid + } + ownerByte := avm.PopByteArray(engine) + owner, err := crypto.DecodePoint(ownerByte) + if err != nil { + return false, err + } + if result, err := s.StateReader.CheckWitnessPublicKey(engine, owner); !result { + return result, err + } + adminByte := avm.PopByteArray(engine) + admin, err := common.Uint160ParseFromBytes(adminByte) + if err != nil { + return false, err + } + issueByte := avm.PopByteArray(engine) + issue, err := common.Uint160ParseFromBytes(issueByte) + if err != nil { + return false, err + } + + assetState := &states.AssetState{ + AssetId: assetId, + AssetType: asset.AssetType(assertType), + Name: string(name), + Amount: common.Fixed64(amount.Int64()), + Precision: byte(precision.Int64()), + Admin: admin, + Issuer: issue, + Owner: owner, + Expiration: ledger.DefaultLedger.Store.GetHeight() + 1 + 2000000, + IsFrozen: false, + } + s.CloneCache.GetInnerCache().GetWriteSet().Add(store.ST_AssetState, string(assetId.ToArray()), assetState) + avm.PushData(engine, assetState) + return true, nil +} + +func (s *StateMachine) CreateContract(engine *avm.ExecutionEngine) (bool, error) { + codeByte := avm.PopByteArray(engine) + if len(codeByte) > 1024*1024 { + return false, nil + } + parameters := avm.PopByteArray(engine) + if len(parameters) > 252 { + return false, nil + } + parameterList := make([]contract.ContractParameterType, 0) + for _, v := range parameters { + parameterList = append(parameterList, contract.ContractParameterType(v)) + } + returnType := avm.PopInt(engine) + nameByte := avm.PopByteArray(engine) + if len(nameByte) > 252 { + return false, nil + } + versionByte := avm.PopByteArray(engine) + if len(versionByte) > 252 { + return false, nil + } + authorByte := avm.PopByteArray(engine) + if len(authorByte) > 252 { + return false, nil + } + emailByte := avm.PopByteArray(engine) + if len(emailByte) > 252 { + return false, nil + } + descByte := avm.PopByteArray(engine) + if len(emailByte) > 65536 { + return false, nil + } + funcCode := &code.FunctionCode{ + Code: codeByte, + ParameterTypes: parameterList, + ReturnType: contract.ContractParameterType(returnType), + } + contractState := &states.ContractState{ + Code: funcCode, + Name: hex.EncodeToString(nameByte), + Version: hex.EncodeToString(versionByte), + Author: hex.EncodeToString(authorByte), + Email: hex.EncodeToString(emailByte), + Description: hex.EncodeToString(descByte), + } + codeHash, err := common.Uint160ParseFromBytes(codeByte) + if err != nil { + return false, err + } + s.CloneCache.GetInnerCache().GetOrAdd(store.ST_Contract, string(codeHash.ToArray()), contractState) + avm.PushData(engine, contractState) + return true, nil +} + +func (s *StateMachine) GetContract(engine *avm.ExecutionEngine) (bool, error) { + hashByte := avm.PopByteArray(engine) + hash, err := common.Uint160ParseFromBytes(hashByte) + if err != nil { + return false, err + } + item, err := s.CloneCache.TryGet(store.ST_Contract, storage.KeyToStr(&hash)) + if err != nil { + return false, err + } + avm.PushData(engine, item.(*states.ContractState)) + return true, nil +} + +func (s *StateMachine) AssetRenew(engine *avm.ExecutionEngine) (bool, error) { + data := avm.PopInteropInterface(engine) + years := avm.PopInt(engine) + at := data.(*states.AssetState) + height := ledger.DefaultLedger.Store.GetHeight() + 1 + b := new(bytes.Buffer) + at.AssetId.Serialize(b) + state, err := s.CloneCache.TryGet(store.ST_AssetState, b.String()) + if err != nil { + return false, err + } + assetState := state.(*states.AssetState) + if assetState.Expiration < height { + assetState.Expiration = height + } + assetState.Expiration += uint32(years) * 2000000 + return true, nil +} + +func (s *StateMachine) ContractDestory(engine *avm.ExecutionEngine) (bool, error) { + data := engine.CurrentContext().CodeHash + if data != nil { + return false, nil + } + hash, err := common.Uint160ParseFromBytes(data) + if err != nil { + return false, err + } + keyStr := storage.KeyToStr(&hash) + item, err := s.CloneCache.TryGet(store.ST_Contract, keyStr) + if err != nil || item == nil { + return false, err + } + s.CloneCache.GetInnerCache().GetWriteSet().Delete(keyStr) + return true, nil +} + +func (s *StateMachine) CheckStorageContext(context *StorageContext) (bool, error) { + item, err := s.CloneCache.TryGet(store.ST_Contract, string(context.codeHash.ToArray())) + if err != nil { + return false, err + } + if item == nil { + return false, fmt.Errorf("check storage context fail, codehash=%v", context.codeHash) + } + return true, nil +} + +func (s *StateMachine) StorageGet(engine *avm.ExecutionEngine) (bool, error) { + opInterface := avm.PopInteropInterface(engine) + context := opInterface.(*StorageContext) + if exist, err := s.CheckStorageContext(context); !exist { + return false, err + } + key := avm.PopByteArray(engine) + storageKey := states.NewStorageKey(context.codeHash, key) + item, err := s.CloneCache.TryGet(store.ST_Storage, storage.KeyToStr(storageKey)) + if err != nil && err.Error() != errors.NewErr("leveldb: not found").Error() { + return false, err + } + if item == nil { + avm.PushData(engine, []byte{}) + } else { + avm.PushData(engine, item.(*states.StorageItem).Value) + } + return true, nil +} + +func (s *StateMachine) StoragePut(engine *avm.ExecutionEngine) (bool, error) { + opInterface := avm.PopInteropInterface(engine) + context := opInterface.(*StorageContext) + key := avm.PopByteArray(engine) + value := avm.PopByteArray(engine) + storageKey := states.NewStorageKey(context.codeHash, key) + s.CloneCache.GetInnerCache().GetWriteSet().Add(store.ST_Storage, storage.KeyToStr(storageKey), states.NewStorageItem(value)) + return true, nil +} + +func (s *StateMachine) StorageDelete(engine *avm.ExecutionEngine) (bool, error) { + opInterface := avm.PopInteropInterface(engine) + context := opInterface.(*StorageContext) + key := avm.PopByteArray(engine) + storageKey := states.NewStorageKey(context.codeHash, key) + s.CloneCache.GetInnerCache().GetWriteSet().Delete(storage.KeyToStr(storageKey)) + return true, nil +} + +func (s *StateMachine) GetStorageContext(engine *avm.ExecutionEngine) (bool, error) { + return true, nil +} + +func contains(programHashes []common.Uint160, programHash common.Uint160) bool { + for _, v := range programHashes { + if v == programHash { + return true + } + } + return false +} diff --git a/smartcontract/service/statereader.go b/smartcontract/service/statereader.go new file mode 100644 index 00000000..fcd6e6e9 --- /dev/null +++ b/smartcontract/service/statereader.go @@ -0,0 +1,684 @@ +package service + +import ( + "DNA/common" + "DNA/core/contract" + "DNA/core/ledger" + "DNA/core/signature" + "DNA/core/transaction" + "DNA/crypto" + "DNA/errors" + "DNA/smartcontract/states" + "DNA/vm/avm" + "DNA/vm/avm/types" + "fmt" + "math/big" +) + +type StateReader struct { + serviceMap map[string]func(*avm.ExecutionEngine) (bool, error) +} + +func NewStateReader() *StateReader { + var stateReader StateReader + stateReader.serviceMap = make(map[string]func(*avm.ExecutionEngine) (bool, error), 0) + stateReader.Register("Neo.Runtime.GetTrigger", stateReader.RuntimeGetTrigger) + stateReader.Register("Neo.Runtime.CheckWitness", stateReader.RuntimeCheckWitness) + stateReader.Register("Neo.Runtime.Notify", stateReader.RuntimeNotify) + stateReader.Register("Neo.Runtime.Log", stateReader.RuntimeLog) + + stateReader.Register("Neo.Blockchain.GetHeight", stateReader.BlockChainGetHeight) + stateReader.Register("Neo.Blockchain.GetHeader", stateReader.BlockChainGetHeader) + stateReader.Register("Neo.Blockchain.GetBlock", stateReader.BlockChainGetBlock) + stateReader.Register("Neo.Blockchain.GetTransaction", stateReader.BlockChainGetTransaction) + stateReader.Register("Neo.Blockchain.GetAccount", stateReader.BlockChainGetAccount) + stateReader.Register("Neo.Blockchain.GetValidators", stateReader.BlockChainGetValidators) + stateReader.Register("Neo.Blockchain.GetAsset", stateReader.BlockChainGetAsset) + + stateReader.Register("Neo.Header.GetHash", stateReader.HeaderGetHash) + stateReader.Register("Neo.Header.GetVersion", stateReader.HeaderGetVersion) + stateReader.Register("Neo.Header.GetPrevHash", stateReader.HeaderGetPrevHash) + stateReader.Register("Neo.Header.GetMerkleRoot", stateReader.HeaderGetMerkleRoot) + stateReader.Register("Neo.Header.GetTimestamp", stateReader.HeaderGetTimestamp) + stateReader.Register("Neo.Header.GetConsensusData", stateReader.HeaderGetConsensusData) + stateReader.Register("Neo.Header.GetNextConsensus", stateReader.HeaderGetNextConsensus) + + stateReader.Register("Neo.Block.GetTransactionCount", stateReader.BlockGetTransactionCount) + stateReader.Register("Neo.Block.GetTransactions", stateReader.BlockGetTransactions) + stateReader.Register("Neo.Block.GetTransaction", stateReader.BlockGetTransaction) + + stateReader.Register("Neo.Transaction.GetHash", stateReader.TransactionGetHash) + stateReader.Register("Neo.Transaction.GetType", stateReader.TransactionGetType) + stateReader.Register("Neo.Transaction.GetAttributes", stateReader.TransactionGetAttributes) + stateReader.Register("Neo.Transaction.GetInputs", stateReader.TransactionGetInputs) + stateReader.Register("Neo.Transaction.GetOutputs", stateReader.TransactionGetOutputs) + stateReader.Register("Neo.Transaction.GetReferences", stateReader.TransactionGetReferences) + + stateReader.Register("Neo.Attribute.GetUsage", stateReader.AttributeGetUsage) + stateReader.Register("Neo.Attribute.GetData", stateReader.AttributeGetData) + + stateReader.Register("Neo.Input.GetHash", stateReader.InputGetHash) + stateReader.Register("Neo.Input.GetIndex", stateReader.InputGetIndex) + + stateReader.Register("Neo.Output.GetAssetId", stateReader.OutputGetAssetId) + stateReader.Register("Neo.Output.GetValue", stateReader.OutputGetValue) + stateReader.Register("Neo.Output.GetScriptHash", stateReader.OutputGetCodeHash) + + stateReader.Register("Neo.Account.GetScriptHash", stateReader.AccountGetCodeHash) + stateReader.Register("Neo.Account.GetBalance", stateReader.AccountGetBalance) + + stateReader.Register("Neo.Asset.GetAssetId", stateReader.AssetGetAssetId) + stateReader.Register("Neo.Asset.GetAssetType", stateReader.AssetGetAssetType) + stateReader.Register("Neo.Asset.GetAmount", stateReader.AssetGetAmount) + stateReader.Register("Neo.Asset.GetAvailable", stateReader.AssetGetAvailable) + stateReader.Register("Neo.Asset.GetPrecision", stateReader.AssetGetPrecision) + stateReader.Register("Neo.Asset.GetOwner", stateReader.AssetGetOwner) + stateReader.Register("Neo.Asset.GetAdmin", stateReader.AssetGetAdmin) + stateReader.Register("Neo.Asset.GetIssuer", stateReader.AssetGetIssuer) + + stateReader.Register("Neo.Contract.GetScript", stateReader.ContractGetCode) + + stateReader.Register("Neo.Storage.GetContext", stateReader.StorageGetContext) + + return &stateReader +} + +func (s *StateReader) Register(methodName string, handler func(*avm.ExecutionEngine) (bool, error)) bool { + if _, ok := s.serviceMap[methodName]; ok { + return false + } + s.serviceMap[methodName] = handler + return true +} + +func (s *StateReader) GetServiceMap() map[string]func(*avm.ExecutionEngine) (bool, error) { + return s.serviceMap +} + +func (s *StateReader) RuntimeGetTrigger(e *avm.ExecutionEngine) (bool, error) { + return true, nil +} + +func (s *StateReader) RuntimeNotify(e *avm.ExecutionEngine) (bool, error) { + avm.PopStackItem(e) + return true, nil +} + +func (s *StateReader) RuntimeLog(e *avm.ExecutionEngine) (bool, error) { + return true, nil +} + +func (s *StateReader) CheckWitnessHash(engine *avm.ExecutionEngine, programHash common.Uint160) (bool, error) { + hashForVerifying, err := engine.GetCodeContainer().(signature.SignableData).GetProgramHashes() + if err != nil { + return false, err + } + return contains(hashForVerifying, programHash), nil +} + +func (s *StateReader) CheckWitnessPublicKey(engine *avm.ExecutionEngine, publicKey *crypto.PubKey) (bool, error) { + c, err := contract.CreateSignatureRedeemScript(publicKey) + if err != nil { + return false, err + } + h, err := common.ToCodeHash(c) + if err != nil { + return false, err + } + return s.CheckWitnessHash(engine, h) +} + +func (s *StateReader) RuntimeCheckWitness(e *avm.ExecutionEngine) (bool, error) { + data := avm.PopByteArray(e) + var ( + result bool + err error + ) + if len(data) == 20 { + program, err := common.Uint160ParseFromBytes(data) + if err != nil { + return false, err + } + result, err = s.CheckWitnessHash(e, program) + } else if len(data) == 33 { + publicKey, err := crypto.DecodePoint(data) + if err != nil { + return false, err + } + result, err = s.CheckWitnessPublicKey(e, publicKey) + } else { + return false, errors.NewDetailErr(err, errors.ErrNoCode, "[RuntimeCheckWitness] data invalid.") + } + if err != nil { + return false, err + } + avm.PushData(e, result) + return true, nil +} + +func (s *StateReader) BlockChainGetHeight(e *avm.ExecutionEngine) (bool, error) { + var i uint32 + if ledger.DefaultLedger == nil { + i = 0 + } else { + i = ledger.DefaultLedger.Store.GetHeight() + } + avm.PushData(e, i) + return true, nil +} + +func (s *StateReader) BlockChainGetHeader(e *avm.ExecutionEngine) (bool, error) { + data := avm.PopByteArray(e) + var ( + header *ledger.Header + err error + ) + l := len(data) + if l <= 5 { + b := new(big.Int) + height := uint32(b.SetBytes(common.ToArrayReverse(data)).Int64()) + if ledger.DefaultLedger != nil { + hash, err := ledger.DefaultLedger.Store.GetBlockHash(height) + if err != nil { + return false, err + } + header, err = ledger.DefaultLedger.Store.GetHeader(hash) + } + } else if l == 32 { + hash, _ := common.Uint256ParseFromBytes(data) + if ledger.DefaultLedger != nil { + header, err = ledger.DefaultLedger.Store.GetHeader(hash) + } + } else { + return false, errors.NewErr("[BlockChainGetHeader] data invalid.") + } + if err != nil { + return false, err + } + avm.PushData(e, header) + return true, nil +} + +func (s *StateReader) BlockChainGetBlock(e *avm.ExecutionEngine) (bool, error) { + data := avm.PopByteArray(e) + var ( + block *ledger.Block + err error + ) + l := len(data) + if l <= 5 { + b := new(big.Int) + height := uint32(b.SetBytes(common.ToArrayReverse(data)).Int64()) + if ledger.DefaultLedger != nil { + hash, err := ledger.DefaultLedger.Store.GetBlockHash(height) + if err != nil { + return false, err + } + block, err = ledger.DefaultLedger.Store.GetBlock(hash) + } + } else if l == 32 { + hash, err := common.Uint256ParseFromBytes(data) + if err != nil { + return false, err + } + if ledger.DefaultLedger != nil { + block, err = ledger.DefaultLedger.Store.GetBlock(hash) + } + } else { + return false, errors.NewErr("[BlockChainGetBlock] data invalid.") + } + if err != nil { + return false, err + } + avm.PushData(e, block) + return true, nil +} + +func (s *StateReader) BlockChainGetTransaction(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopByteArray(e) + hash, err := common.Uint256ParseFromBytes(d) + if err != nil { + return false, err + } + tx, err := ledger.DefaultLedger.Store.GetTransaction(hash) + if err != nil { + return false, err + } + + avm.PushData(e, tx) + return true, nil +} + +func (s *StateReader) BlockChainGetAccount(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopByteArray(e) + hash, err := common.Uint160ParseFromBytes(d) + if err != nil { + return false, err + } + account, err := ledger.DefaultLedger.Store.GetAccount(hash) + avm.PushData(e, account) + return true, nil +} + +func (s *StateReader) BlockChainGetAsset(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopByteArray(e) + hash, err := common.Uint256ParseFromBytes(d) + if err != nil { + return false, err + } + assetState, err := ledger.DefaultLedger.Store.GetAssetState(hash) + if err != nil { + return false, err + } + avm.PushData(e, assetState) + return true, nil +} + +func (s *StateReader) BlockChainGetValidators(e *avm.ExecutionEngine) (bool, error) { + bookKeeperList, _, err := ledger.DefaultLedger.Store.GetBookKeeperList() + if err != nil { + return false, err + } + pkList := make([]types.StackItemInterface, 0) + for _, v := range bookKeeperList { + pk, err := v.EncodePoint(true) + if err != nil { + return false, err + } + pkList = append(pkList, types.NewByteArray(pk)) + } + avm.PushData(e, pkList) + return true, nil +} + +func (s *StateReader) HeaderGetHash(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get header error in function headergethash!") + } + h := d.(*ledger.Header).Blockdata.Hash() + avm.PushData(e, h.ToArray()) + return true, nil +} + +func (s *StateReader) HeaderGetVersion(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get header error in function headergetversion") + } + version := d.(*ledger.Header).Blockdata.Version + avm.PushData(e, version) + return true, nil +} + +func (s *StateReader) HeaderGetPrevHash(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get header error in function HeaderGetPrevHash") + } + preHash := d.(*ledger.Header).Blockdata.PrevBlockHash + avm.PushData(e, preHash.ToArray()) + return true, nil +} + +func (s *StateReader) HeaderGetMerkleRoot(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get header error in function HeaderGetMerkleRoot") + } + root := d.(*ledger.Header).Blockdata.TransactionsRoot + avm.PushData(e, root.ToArray()) + return true, nil +} + +func (s *StateReader) HeaderGetTimestamp(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get header error in function HeaderGetTimestamp") + } + timeStamp := d.(*ledger.Header).Blockdata.Timestamp + avm.PushData(e, timeStamp) + return true, nil +} + +func (s *StateReader) HeaderGetConsensusData(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get header error in function HeaderGetConsensusData") + } + consensusData := d.(*ledger.Header).Blockdata.ConsensusData + avm.PushData(e, consensusData) + return true, nil +} + +func (s *StateReader) HeaderGetNextConsensus(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get header error in function HeaderGetNextConsensus") + } + nextBookKeeper := d.(*ledger.Header).Blockdata.NextBookKeeper + avm.PushData(e, nextBookKeeper.ToArray()) + return true, nil +} + +func (s *StateReader) BlockGetTransactionCount(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get block error in function BlockGetTransactionCount") + } + transactions := d.(*ledger.Block).Transactions + avm.PushData(e, len(transactions)) + return true, nil +} + +func (s *StateReader) BlockGetTransactions(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get block data error in function BlockGetTransactions") + } + transactions := d.(*ledger.Block).Transactions + transactionList := make([]types.StackItemInterface, 0) + for _, v := range transactions { + transactionList = append(transactionList, types.NewInteropInterface(v)) + } + avm.PushData(e, transactionList) + return true, nil +} + +func (s *StateReader) BlockGetTransaction(e *avm.ExecutionEngine) (bool, error) { + index := avm.PopInt(e) + if index < 0 { + return false, fmt.Errorf("%v", "index invalid in function BlockGetTransaction") + } + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get transaction data error in function BlockGetTransaction") + } + transactions := d.(*ledger.Block).Transactions + if index >= len(transactions) { + return false, fmt.Errorf("%v", "index over transaction length in function BlockGetTransaction") + } + avm.PushData(e, transactions[index]) + return true, nil +} + +func (s *StateReader) TransactionGetHash(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get transaction error in function TransactionGetHash") + } + txHash := d.(*transaction.Transaction).Hash() + avm.PushData(e, txHash.ToArray()) + return true, nil +} + +func (s *StateReader) TransactionGetType(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get transaction error in function TransactionGetType") + } + txType := d.(*transaction.Transaction).TxType + avm.PushData(e, int(txType)) + return true, nil +} + +func (s *StateReader) TransactionGetAttributes(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get transaction error in function TransactionGetAttributes") + } + attributes := d.(*transaction.Transaction).Attributes + attributList := make([]types.StackItemInterface, 0) + for _, v := range attributes { + attributList = append(attributList, types.NewInteropInterface(v)) + } + avm.PushData(e, attributList) + return true, nil +} + +func (s *StateReader) TransactionGetInputs(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get transaction error in function TransactionGetInputs") + } + inputs := d.(*transaction.Transaction).UTXOInputs + inputList := make([]types.StackItemInterface, 0) + for _, v := range inputs { + inputList = append(inputList, types.NewInteropInterface(v)) + } + avm.PushData(e, inputList) + return true, nil +} + +func (s *StateReader) TransactionGetOutputs(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get transaction error in function TransactionGetOutputs") + } + outputs := d.(*transaction.Transaction).Outputs + outputList := make([]types.StackItemInterface, 0) + for _, v := range outputs { + outputList = append(outputList, types.NewInteropInterface(v)) + } + avm.PushData(e, outputList) + return true, nil +} + +func (s *StateReader) TransactionGetReferences(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get transaction error in function TransactionGetReferences") + } + references, err := d.(*transaction.Transaction).GetReference() + referenceList := make([]types.StackItemInterface, 0) + for _, v := range references { + referenceList = append(referenceList, types.NewInteropInterface(v)) + } + avm.PushData(e, referenceList) + if err != nil { + return false, err + } + return true, nil +} + +func (s *StateReader) AttributeGetUsage(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get Attribute error in function AttributeGetUsage") + } + attribute := d.(*transaction.TxAttribute) + avm.PushData(e, int(attribute.Usage)) + return true, nil +} + +func (s *StateReader) AttributeGetData(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get Attribute error in function AttributeGetUsage") + } + attribute := d.(*transaction.TxAttribute) + avm.PushData(e, attribute.Data) + return true, nil +} + +func (s *StateReader) InputGetHash(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get UTXOTxInput error in function InputGetHash") + } + input := d.(*transaction.UTXOTxInput) + avm.PushData(e, input.ReferTxID.ToArray()) + return true, nil +} + +func (s *StateReader) InputGetIndex(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get transaction error in function TransactionGetReferences") + } + input := d.(*transaction.UTXOTxInput) + avm.PushData(e, input.ReferTxOutputIndex) + return true, nil +} + +func (s *StateReader) OutputGetAssetId(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get TxOutput error in function OutputGetAssetId") + } + output := d.(*transaction.TxOutput) + avm.PushData(e, output.AssetID.ToArray()) + return true, nil +} + +func (s *StateReader) OutputGetValue(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get TxOutput error in function OutputGetValue") + } + output := d.(*transaction.TxOutput) + avm.PushData(e, output.Value.GetData()) + return true, nil +} + +func (s *StateReader) OutputGetCodeHash(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get TxOutput error in function OutputGetCodeHash") + } + output := d.(*transaction.TxOutput) + avm.PushData(e, output.ProgramHash.ToArray()) + return true, nil +} + +func (s *StateReader) AccountGetCodeHash(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AccountState error in function AccountGetCodeHash") + } + accountState := d.(*states.AccountState).ProgramHash + avm.PushData(e, accountState.ToArray()) + return true, nil +} + +func (s *StateReader) AccountGetBalance(e *avm.ExecutionEngine) (bool, error) { + assetIdByte := avm.PopByteArray(e) + assetId, err := common.Uint256ParseFromBytes(assetIdByte) + if err != nil { + return false, err + } + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AccountState error in function AccountGetCodeHash") + } + accountState := d.(*states.AccountState) + balance := common.Fixed64(0) + if v, ok := accountState.Balances[assetId]; ok { + balance = v + } + avm.PushData(e, balance.GetData()) + return true, nil +} + +func (s *StateReader) AssetGetAssetId(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AssetState error in function AssetGetAssetId") + } + assetState := d.(*states.AssetState) + avm.PushData(e, assetState.AssetId.ToArray()) + return true, nil +} + +func (s *StateReader) AssetGetAssetType(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AssetState error in function AssetGetAssetType") + } + assetState := d.(*states.AssetState) + avm.PushData(e, int(assetState.AssetType)) + return true, nil +} + +func (s *StateReader) AssetGetAmount(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AssetState error in function AssetGetAmount") + } + assetState := d.(*states.AssetState) + avm.PushData(e, assetState.Amount.GetData()) + return true, nil +} + +func (s *StateReader) AssetGetAvailable(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AssetState error in function AssetGetAvailable") + } + assetState := d.(*states.AssetState) + avm.PushData(e, assetState.Available.GetData()) + return true, nil +} + +func (s *StateReader) AssetGetPrecision(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AssetState error in function AssetGetPrecision") + } + assetState := d.(*states.AssetState) + avm.PushData(e, int(assetState.Precision)) + return true, nil +} + +func (s *StateReader) AssetGetOwner(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AssetState error in function AssetGetOwner") + } + assetState := d.(*states.AssetState) + owner, err := assetState.Owner.EncodePoint(true) + if err != nil { + return false, err + } + avm.PushData(e, owner) + return true, nil +} + +func (s *StateReader) AssetGetAdmin(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AssetState error in function AssetGetAdmin") + } + assetState := d.(*states.AssetState) + avm.PushData(e, assetState.Admin.ToArray()) + return true, nil +} + +func (s *StateReader) AssetGetIssuer(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get AssetState error in function AssetGetIssuer") + } + assetState := d.(*states.AssetState) + avm.PushData(e, assetState.Issuer.ToArray()) + return true, nil +} + +func (s *StateReader) ContractGetCode(e *avm.ExecutionEngine) (bool, error) { + d := avm.PopInteropInterface(e) + if d == nil { + return false, fmt.Errorf("%v", "Get ContractState error in function ContractGetCode") + } + assetState := d.(*states.ContractState) + avm.PushData(e, assetState.Code.Code) + return true, nil +} + +func (s *StateReader) StorageGetContext(e *avm.ExecutionEngine) (bool, error) { + codeHash, err := common.Uint160ParseFromBytes(e.CurrentContext().GetCodeHash()) + if err != nil { + return false, err + } + avm.PushData(e, NewStorageContext(&codeHash)) + return true, nil +} diff --git a/smartcontract/service/storagecontext.go b/smartcontract/service/storagecontext.go new file mode 100644 index 00000000..16e519e8 --- /dev/null +++ b/smartcontract/service/storagecontext.go @@ -0,0 +1,19 @@ +package service + +import ( + "DNA/common" +) + +type StorageContext struct { + codeHash *common.Uint160 +} + +func NewStorageContext(codeHash *common.Uint160) *StorageContext { + var storageContext StorageContext + storageContext.codeHash = codeHash + return &storageContext +} + +func (sc *StorageContext) ToArray() []byte { + return sc.codeHash.ToArray() +} diff --git a/smartcontract/service/types.go b/smartcontract/service/types.go new file mode 100644 index 00000000..74b6e5fb --- /dev/null +++ b/smartcontract/service/types.go @@ -0,0 +1,102 @@ +package service + +import ( + . "DNA/common" + "DNA/core/asset" + "DNA/core/ledger" + "DNA/core/transaction" + . "DNA/net/httpjsonrpc" + "DNA/net/httprestful/common" + "DNA/smartcontract/states" +) + +type AccountInfo struct { + ProgramHash string + IsFrozen bool + Balances map[string]Fixed64 +} + +type AssetInfo struct { + Name string + Precision byte + AssetType byte + RecordType byte +} + +func GetHeaderInfo(header *ledger.Header) *BlockHead { + h := header.Blockdata.Hash() + return &BlockHead{ + Version: header.Blockdata.Version, + PrevBlockHash: ToHexString(header.Blockdata.PrevBlockHash.ToArrayReverse()), + TransactionsRoot: ToHexString(header.Blockdata.TransactionsRoot.ToArrayReverse()), + Timestamp: header.Blockdata.Timestamp, + Height: header.Blockdata.Height, + ConsensusData: header.Blockdata.ConsensusData, + NextBookKeeper: ToHexString(header.Blockdata.NextBookKeeper.ToArrayReverse()), + Program: ProgramInfo{ + Code: ToHexString(header.Blockdata.Program.Code), + Parameter: ToHexString(header.Blockdata.Program.Parameter), + }, + Hash: ToHexString(h.ToArrayReverse()), + } + +} + +func GetBlockInfo(block *ledger.Block) *BlockInfo { + blockInfo := common.GetBlockInfo(block) + return &blockInfo +} + +func GetTransactionInfo(transaction *transaction.Transaction) *Transactions { + return TransArryByteToHexString(transaction) +} + +func GetTransactionInputs(inputs []*transaction.UTXOTxInput) []UTXOTxInputInfo { + inputList := make([]UTXOTxInputInfo, len(inputs)) + for k, v := range inputs { + inputList[k].ReferTxID = ToHexString(v.ReferTxID.ToArrayReverse()) + inputList[k].ReferTxOutputIndex = v.ReferTxOutputIndex + } + return inputList +} + +func GetTransactionOutputs(outputs []*transaction.TxOutput) []TxoutputInfo { + outputList := make([]TxoutputInfo, len(outputs)) + for k, v := range outputs { + outputList[k].AssetID = ToHexString(v.AssetID.ToArrayReverse()) + outputList[k].Value = v.Value + outputList[k].ProgramHash = ToHexString(v.ProgramHash.ToArrayReverse()) + } + return outputList +} + +func GetTransactionAttributes(attributes []*transaction.TxAttribute) []TxAttributeInfo { + attributeList := make([]TxAttributeInfo, len(attributes)) + for k, v := range attributes { + attributeList[k].Usage = v.Usage + attributeList[k].Data = ToHexString(v.Data) + } + return attributeList +} + +func GetAccountInfo(account *states.AccountState) *AccountInfo { + balances := make(map[string]Fixed64) + for k, v := range account.Balances { + assetId := ToHexString(k.ToArrayReverse()) + balances[assetId] = v + } + return &AccountInfo{ + ProgramHash: ToHexString(account.ProgramHash.ToArrayReverse()), + IsFrozen: account.IsFrozen, + Balances: balances, + } +} + +func GetAssetInfo(asset *asset.Asset) *AssetInfo { + return &AssetInfo{ + Name: asset.Name, + Precision: asset.Precision, + AssetType: byte(asset.AssetType), + RecordType: byte(asset.RecordType), + } +} diff --git a/smartcontract/smartcontract.go b/smartcontract/smartcontract.go new file mode 100644 index 00000000..d17d468d --- /dev/null +++ b/smartcontract/smartcontract.go @@ -0,0 +1,235 @@ +package smartcontract + +import ( + "DNA/common" + "DNA/common/serialization" + "DNA/core/asset" + "DNA/core/contract" + "DNA/core/ledger" + sig "DNA/core/signature" + "DNA/core/transaction" + "DNA/errors" + "DNA/smartcontract/service" + "DNA/smartcontract/states" + "DNA/smartcontract/storage" + "DNA/smartcontract/types" + "DNA/vm/avm" + "DNA/vm/avm/interfaces" + . "DNA/vm/avm/types" + "DNA/vm/evm" + "DNA/vm/evm/abi" + "bytes" + "math/big" + "strconv" +) + +type SmartContract struct { + Engine Engine + Code []byte + Input []byte + ParameterTypes []contract.ContractParameterType + ABI abi.ABI + Caller common.Uint160 + CodeHash common.Uint160 + VMType types.VmType + ReturnType contract.ContractParameterType +} + +type Context struct { + Language types.LangType + Caller common.Uint160 + StateMachine *service.StateMachine + DBCache storage.DBCache + Code []byte + Input []byte + CodeHash common.Uint160 + Time *big.Int + BlockNumber *big.Int + CacheCodeTable interfaces.ICodeTable + SignableData sig.SignableData + Gas common.Fixed64 + ReturnType contract.ContractParameterType + ParameterTypes []contract.ContractParameterType +} + +type Engine interface { + Create(caller common.Uint160, code []byte) ([]byte, error) + Call(caller common.Uint160, codeHash common.Uint160, input []byte) ([]byte, error) +} + +func NewSmartContract(context *Context) (*SmartContract, error) { + if vmType, ok := types.LangVm[context.Language]; ok { + var e Engine + switch vmType { + case types.AVM: + e = avm.NewExecutionEngine( + context.SignableData, + new(avm.ECDsaCrypto), + context.CacheCodeTable, + context.StateMachine, + context.Gas, + ) + case types.EVM: + e = evm.NewExecutionEngine(context.DBCache, context.Time, context.BlockNumber, context.Gas) + } + + return &SmartContract{ + Engine: e, + Code: context.Code, + CodeHash: context.CodeHash, + Input: context.Input, + Caller: context.Caller, + VMType: vmType, + ReturnType: context.ReturnType, + ParameterTypes: context.ParameterTypes, + }, nil + } else { + return nil, errors.NewDetailErr(errors.NewErr("Not Support Language Type!"), errors.ErrNoCode, "") + } + +} + +func (sc *SmartContract) DeployContract() ([]byte, error) { + return sc.Engine.Create(sc.Caller, sc.Code) +} + +func (sc *SmartContract) InvokeContract() (interface{}, error) { + //input, err := sc.InvokeParamsTransform() + //if err != nil { + // return nil, err + //} + _, err := sc.Engine.Call(sc.Caller, sc.CodeHash, sc.Input) + if err != nil { + return nil, err + } + return sc.InvokeResult() +} + +func (sc *SmartContract) InvokeResult() (interface{}, error) { + switch sc.VMType { + case types.AVM: + engine := sc.Engine.(*avm.ExecutionEngine) + if engine.GetEvaluationStackCount() > 0 && avm.Peek(engine).GetStackItem() != nil { + switch sc.ReturnType { + case contract.Boolean: + return avm.PopBoolean(engine), nil + case contract.Integer: + return avm.PopBigInt(engine).String(), nil + case contract.ByteArray: + bs := avm.PopByteArray(engine) + return common.BytesToInt(bs), nil + case contract.String: + return string(avm.PopByteArray(engine)), nil + case contract.Hash160, contract.Hash256: + return common.ToHexString(common.ToArrayReverse(avm.PopByteArray(engine))), nil + case contract.PublicKey: + return common.ToHexString(avm.PopByteArray(engine)), nil + case contract.Object: + data := avm.PeekStackItem(engine) + switch data.(type) { + case *Boolean: + return data.GetBoolean(), nil + case *Integer: + return data.GetBigInteger(), nil + case *ByteArray: + return common.BytesToInt(data.GetByteArray()), nil + case *InteropInterface: + interop := data.GetInterface() + switch interop.(type) { + case *ledger.Header: + return service.GetHeaderInfo(interop.(*ledger.Header)), nil + case *ledger.Block: + return service.GetBlockInfo(interop.(*ledger.Block)), nil + case *transaction.Transaction: + return service.GetTransactionInfo(interop.(*transaction.Transaction)), nil + case *states.AccountState: + return service.GetAccountInfo(interop.(*states.AccountState)), nil + case *asset.Asset: + return service.GetAssetInfo(interop.(*asset.Asset)), nil + } + } + //case contract.Object: + // data := avm.PeekInteropInterface(engine) + // switch data.(type) { + // case *ledger.Header: + // return service.GetHeaderInfo(data.(*ledger.Header)), nil + // case *ledger.Block: + // return service.GetBlockInfo(data.(*ledger.Block)), nil + // case *transaction.Transaction: + // return service.GetTransactionInfo(data.(*transaction.Transaction)), nil + // case *states.AccountState: + // return service.GetAccountInfo(data.(*states.AccountState)), nil + // case *asset.Asset: + // return service.GetAssetInfo(data.(*asset.Asset)), nil + // default: + // return data, nil + //data := avm.PeekArray(engine) + //if len(data) == 0 { + // return nil, nil + //} + //switch data[0].(type) { + //case *transaction.TxAttribute: + // attributs := make([]*transaction.TxAttribute, len(data)) + // for k, v := range data { + // attributs[k] = v(*transaction.TxAttribute{}) + // } + // return service.GetTransactionAttributes(attributs) + //} + //return data, nil + //} + } + } + case types.EVM: + } + return nil, nil +} + +func (sc *SmartContract) InvokeParamsTransform() ([]byte, error) { + switch sc.VMType { + case types.AVM: + builder := avm.NewParamsBuilder(new(bytes.Buffer)) + b := bytes.NewBuffer(sc.Input) + for _, k := range sc.ParameterTypes { + switch k { + case contract.Boolean: + p, err := serialization.ReadBool(b) + if err != nil { + return nil, err + } + builder.EmitPushBool(p) + case contract.Integer: + p, err := serialization.ReadVarBytes(b) + if err != nil { + return nil, err + } + i, err := strconv.ParseInt(string(p), 10, 64) + if err != nil { + return nil, err + } + builder.EmitPushInteger(int64(i)) + case contract.Hash160, contract.Hash256: + p, err := serialization.ReadVarBytes(b) + if err != nil { + return nil, err + } + builder.EmitPushByteArray(common.ToArrayReverse(p)) + case contract.ByteArray, contract.String: + p, err := serialization.ReadVarBytes(b) + if err != nil { + return nil, err + } + builder.EmitPushByteArray(p) + case contract.Array: + //val, err := serialization.ReadVarUint(b, 0) + //if err != nil { + // return nil, err + //} + + } + } + builder.EmitPushCall(sc.CodeHash.ToArray()) + return builder.ToArray(), nil + case types.EVM: + } + return nil, nil +} diff --git a/smartcontract/states/accountstate.go b/smartcontract/states/accountstate.go new file mode 100644 index 00000000..a2e53dac --- /dev/null +++ b/smartcontract/states/accountstate.go @@ -0,0 +1,62 @@ +package states + +import ( + "DNA/common" + "io" + "bytes" + "DNA/common/serialization" +) + +type AccountState struct { + ProgramHash common.Uint160 + IsFrozen bool + Balances map[common.Uint256]common.Fixed64 +} + +func NewAccountState(programHash common.Uint160, balances map[common.Uint256]common.Fixed64) *AccountState { + var accountState AccountState + accountState.ProgramHash = programHash + accountState.Balances = balances + accountState.IsFrozen = false + return &accountState +} + +func(accountState *AccountState)Serialize(w io.Writer) error { + accountState.ProgramHash.Serialize(w) + serialization.WriteBool(w, accountState.IsFrozen) + serialization.WriteUint64(w, uint64(len(accountState.Balances))) + for k, v := range accountState.Balances { + k.Serialize(w) + v.Serialize(w) + } + return nil +} + +func(accountState *AccountState)Deserialize(r io.Reader) error { + accountState.ProgramHash.Deserialize(r) + isFrozen, err := serialization.ReadBool(r) + if err != nil { return err } + accountState.IsFrozen = isFrozen + l, err := serialization.ReadUint64(r) + if err != nil { return err } + balances := make(map[common.Uint256]common.Fixed64, 0) + u := new(common.Uint256) + f := new(common.Fixed64) + for i:=0; i= 0 && i < len(c) && j >= 0 && j < len(c) { // Unit Test modify + c[i], c[j] = c[j], c[i] + } +} +func (c BigIntSorter) Less(i, j int) bool { + if i >= 0 && i < len(c) && j >= 0 && j < len(c) { // Unit Test modify + return c[i].Cmp(&c[j]) < 0 + } + + return false +} + +func ToBigInt(data interface{}) *big.Int { + var bi big.Int + switch t := data.(type) { + case int64: + bi.SetInt64(int64(t)) + case int32: + bi.SetInt64(int64(t)) + case int16: + bi.SetInt64(int64(t)) + case int8: + bi.SetInt64(int64(t)) + case int: + bi.SetInt64(int64(t)) + case uint64: + bi.SetUint64(uint64(t)) + case uint32: + bi.SetUint64(uint64(t)) + case uint16: + bi.SetUint64(uint64(t)) + case uint8: + bi.SetUint64(uint64(t)) + case uint: + bi.SetUint64(uint64(t)) + case big.Int: + bi = t + case *big.Int: + bi = *t + } + return &bi +} + +//common func +func SumBigInt(ints []big.Int) big.Int { + sum := big.NewInt(0) + for _, v := range ints { + sum = sum.Add(sum, &v) + } + return *sum +} + +func MinBigInt(ints []big.Int) big.Int{ + minimum := ints[0] + + for _, d := range ints { + if d.Cmp(&minimum) < 0 { + minimum = d + } + } + + return minimum +} + +func MaxBigInt(ints []big.Int) big.Int{ + max := ints[0] + + for _, d := range ints { + if d.Cmp(&max) > 0 { + max = d + } + } + + return max +} + +func MinInt64(datas []int64) int64 { + + var minimum int64 + for i, d := range datas { // Unit Test modify + if i == 0 { + minimum = d + } + if d < minimum { + minimum = d + } + } + + return minimum +} + +func MaxInt64(datas []int64) int64 { + + var maximum int64 + //i := 0 + for i, d := range datas { // Unit Test modify + if i == 0 { + maximum = d + //i++ + } + if d > maximum { + maximum = d + } + } + + return maximum +} + +func Concat(array1 []byte, array2 []byte) []byte { + len := len(array2) + for i := 0; i < len; i++ { + array1 = append(array1, array2[i]) // Unit Test modify + } + + return array1 +} + + +func BigIntOp(bi *big.Int, op OpCode) *big.Int { + nb := new(big.Int) + switch op { + case INC: + nb.Add(bi, big.NewInt(int64(1))) + case DEC: + nb.Sub(bi, big.NewInt(int64(1))) + case NEGATE: + nb.Neg(bi) + case ABS: + nb.Abs(bi) + default: + nb = bi + } + return nb +} + +func AsBool(e interface{}) bool { + if v, ok := e.([]byte); ok { + for _, b := range v { + if b != 0 { + return true + } + } + } + return false +} + +func AsInt64(b []byte) (int64, error) { + if len(b) == 0 { + return 0, nil + } + if len(b) > 8 { + return 0, errors.ErrBadValue + } + + var bs [8]byte + copy(bs[:], b) + + res := binary.LittleEndian.Uint64(bs[:]) + + return int64(res), nil +} + +func ByteArrZip(s1 []byte, s2 []byte, op OpCode) []byte{ + var ns []byte + switch op { + case CAT: + ns = append(s1, s2...) + } + return ns +} + +func BigIntZip(ints1 *big.Int, ints2 *big.Int, op OpCode) *big.Int { + nb := new(big.Int) + switch op { + case AND: + nb.And(ints1, ints2) + case OR: + nb.Or(ints1, ints2) + case XOR: + nb.Xor(ints1, ints2) + case ADD: + nb.Add(ints1, ints2) + case SUB: + nb.Sub(ints1, ints2) + case MUL: + nb.Mul(ints1, ints2) + case DIV: + if ints2.Sign() == 0 { + return nb + } + nb.Div(ints1, ints2) + case MOD: + if ints2.Sign() == 0 { + return nb + } + nb.Mod(ints1, ints2) + case SHL: + nb.Lsh(ints1, uint(ints2.Int64())) + case SHR: + nb.Rsh(ints1, uint(ints2.Int64())) + case MIN: + c := ints1.Cmp(ints2) + if c <= 0 { + nb = ints1 + } else { + nb = ints2 + } + case MAX: + c := ints1.Cmp(ints2) + if c <= 0 { + nb = ints2 + } else { + nb = ints1 + } + } + return nb +} + +func BigIntComp(bigint *big.Int, op OpCode) bool { + var nb bool + switch op { + case NZ: + nb = bigint.Cmp(big.NewInt(int64(0))) != 0 + } + return nb +} + +func BigIntMultiComp(ints1 *big.Int, ints2 *big.Int, op OpCode) bool { + var nb bool + switch op { + case NUMEQUAL: + nb = ints1.Cmp(ints2) == 0 + case NUMNOTEQUAL: + nb = ints1.Cmp(ints2) != 0 + case LT: + nb = ints1.Cmp(ints2) < 0 + case GT: + nb = ints1.Cmp(ints2) > 0 + case LTE: + nb = ints1.Cmp(ints2) <= 0 + case GTE: + nb = ints1.Cmp(ints2) >= 0 + } + return nb +} + +func BoolZip(bi1 bool, bi2 bool, op OpCode) bool { + var nb bool + switch op { + case BOOLAND: + nb = bi1 && bi2 + case BOOLOR: + nb = bi1 || bi2 + } + return nb +} + +func BoolArrayOp(bools []bool, op OpCode) []bool { + bls := []bool{} + for _, b := range bools { + var nb bool + + switch op { + case NOT: + nb = !b + default: + nb = b + } + bls = append(bls, nb) + } + + return bls +} + +func IsEqualBytes(b1 []byte, b2 []byte) bool { + len1 := len(b1) + len2 := len(b2) + if len1 != len2 { + return false + } + + for i := 0; i < len1; i++ { + if b1[i] != b2[i] { + return false + } + } + + return true +} + +func IsEqual(v1 interface{}, v2 interface{}) bool { + + if reflect.TypeOf(v1) != reflect.TypeOf(v2) { + return false + } + switch t1 := v1.(type) { + case []byte: + switch t2 := v2.(type) { + case []byte: + return IsEqualBytes(t1, t2) + } + case int8, int16, int32, int64: + if v1 == v2 { + return true + } + return false + default: + return false + } + + return false +} + +func WithInOp(int1 *big.Int, int2 *big.Int, int3 *big.Int) bool { + b1 := BigIntMultiComp(int1, int2, GTE) + b2 := BigIntMultiComp(int1, int3, LT) + return BoolZip(b1, b2, BOOLAND) +} + +func NewStackItems() []types.StackItemInterface { + return make([]types.StackItemInterface, 0) +} + +func NewStackItemInterface(data interface{}) types.StackItemInterface { + var stackItem types.StackItemInterface + switch data.(type) { + case int8, int16, int32, int64, int, uint8, uint16, uint32, uint64, *big.Int, big.Int: + stackItem = types.NewInteger(ToBigInt(data)) + case *types.Integer: + stackItem = data.(*types.Integer) + case *types.Array: + stackItem = data.(*types.Array) + case *types.Boolean: + stackItem = data.(*types.Boolean) + case *types.ByteArray: + stackItem = data.(*types.ByteArray) + case bool: + stackItem = types.NewBoolean(data.(bool)) + case []byte: + stackItem = types.NewByteArray(data.([]byte)) + case []types.StackItemInterface: + stackItem = types.NewArray(data.([]types.StackItemInterface)) + case types.StackItemInterface: + stackItem = data.(types.StackItemInterface) + case interfaces.IInteropInterface: + stackItem = types.NewInteropInterface(data.(interfaces.IInteropInterface)) + default: + fmt.Println("=======================type invalid==============================") + } + return stackItem +} + +func Hash(b []byte, e *ExecutionEngine) []byte { + var sh hash.Hash + var bt []byte + switch e.opCode { + case SHA1: + sh = sha1.New() + sh.Write(b) + bt = sh.Sum(nil) + case SHA256: + sh = sha256.New() + sh.Write(b) + bt = sh.Sum(nil) + case HASH160: + bt = e.crypto.Hash160(b) + case HASH256: + bt = e.crypto.Hash256(b) + } + return bt +} + + + +func PopBigInt(e *ExecutionEngine) *big.Int { + x := PopStackItem(e) + return x.GetBigInteger() +} + +func PopInt(e *ExecutionEngine) int { + x:= PopBigInt(e) + n := int(x.Int64()) + return n +} + + +func PopBoolean(e *ExecutionEngine) bool { + x := PopStackItem(e) + return x.GetBoolean() +} + +func PopArray(e *ExecutionEngine) []types.StackItemInterface { + x := PopStackItem(e) + return x.GetArray() +} + +func PopInteropInterface(e *ExecutionEngine) interfaces.IInteropInterface { + x := PopStackItem(e) + return x.GetInterface() +} + +func PopByteArray(e *ExecutionEngine) []byte { + x := PopStackItem(e) + return x.GetByteArray() +} + +func PopStackItem(e *ExecutionEngine) types.StackItemInterface { + return Pop(e).GetStackItem() +} + +func Pop(e *ExecutionEngine) Element { + return e.evaluationStack.Pop() +} + +func PeekArray(e *ExecutionEngine) []types.StackItemInterface { + x := PeekStackItem(e) + return x.GetArray() +} + +func PeekInteropInterface(e *ExecutionEngine) interfaces.IInteropInterface { + x := PeekStackItem(e) + return x.GetInterface() +} + +func PeekInt(e *ExecutionEngine) int { + x := PeekBigInteger(e) + n := int(x.Int64()) + return n +} + +func PeekBigInteger(e *ExecutionEngine) *big.Int { + x := PeekStackItem(e) + return x.GetBigInteger() +} + +func PeekStackItem(e *ExecutionEngine) types.StackItemInterface { + return Peek(e).GetStackItem() +} + +func PeekNInt(i int, e *ExecutionEngine) int { + x := PeekNBigInt(i, e) + n := int(x.Int64()) + return n +} + +func PeekNBigInt(i int, e *ExecutionEngine) *big.Int { + x := PeekNStackItem(i, e) + return x.GetBigInteger() +} + +func PeekNByteArray(i int, e *ExecutionEngine) []byte { + x := PeekNStackItem(i, e) + return x.GetByteArray() +} + +func PeekNStackItem(i int, e *ExecutionEngine) types.StackItemInterface { + return PeekN(i, e).GetStackItem() +} + +func PeekN(i int, e *ExecutionEngine) Element { + return e.evaluationStack.Peek(i) +} + +func Peek(e *ExecutionEngine) Element { + return e.evaluationStack.Peek(0) +} + +func EvaluationStackCount(e *ExecutionEngine) int { + return e.evaluationStack.Count() +} + +func Push(e *ExecutionEngine, element Element) { + e.evaluationStack.Push(element) +} + +func Count(e *ExecutionEngine) int { + return e.evaluationStack.Count() +} + +func PushData(e *ExecutionEngine, data interface{}) { + d := NewStackItemInterface(data) + e.evaluationStack.Push(NewStackItem(d)) +} + + + diff --git a/vm/avm/common_test.go b/vm/avm/common_test.go new file mode 100644 index 00000000..dba81190 --- /dev/null +++ b/vm/avm/common_test.go @@ -0,0 +1,23 @@ +package avm + +import ( + "math/big" + "testing" + "fmt" +) + +func TestCommon(t *testing.T) { + i := ToBigInt(big.NewInt(1)) + t.Log("i", i) + + fmt.Println(ToArrayReverse([]byte{1, 2, 3})) +} + +func ToArrayReverse(arr []byte) []byte { + l := len(arr) + x := make([]byte, 0) + for i := l - 1; i >= 0 ;i--{ + x = append(x, arr[i]) + } + return x +} diff --git a/vm/avm/doc.go b/vm/avm/doc.go new file mode 100644 index 00000000..25d51e14 --- /dev/null +++ b/vm/avm/doc.go @@ -0,0 +1,14 @@ +/* +Copyright 2016 The DNA Dev team + +The go-vm library is free software: you can redistribute it and/or modify +it under the terms of the APACHA License + +package vm implemented a blockchain virtual machine, which provide following main functionaries: + +- opcode VM support the logic of opcode script +- apiservice for external interaction API with triggered by opcode +- crypto for external crypto implementations with triggered by opcode +- + */ +package avm diff --git a/vm/avm/errors/errors.go b/vm/avm/errors/errors.go new file mode 100644 index 00000000..cc5911b9 --- /dev/null +++ b/vm/avm/errors/errors.go @@ -0,0 +1,22 @@ +package errors + + +import "errors" + +var ( + ErrBadValue = errors.New("bad value") + ErrBadType = errors.New("bad type") + ErrOverStackLen = errors.New("the count over the stack length") + ErrOverCodeLen = errors.New("the count over the code length") + ErrUnderStackLen = errors.New("the count under the stack length") + ErrFault = errors.New("the exeution meet fault") + ErrNotSupportService = errors.New("the service is not registered") + ErrNotSupportOpCode = errors.New("does not support the operation code") + ErrOverLimitStack = errors.New("the stack over max size") + ErrOverMaxItemSize = errors.New("the item over max size") + ErrOverMaxArraySize = errors.New("the array over max size") + ErrOutOfGas = errors.New("out of gas") + ErrNotArray = errors.New("not array") + ErrTableIsNil = errors.New("table is nil") + ErrServiceIsNil = errors.New("service is nil") +) diff --git a/vm/avm/execution_context.go b/vm/avm/execution_context.go new file mode 100644 index 00000000..714fb818 --- /dev/null +++ b/vm/avm/execution_context.go @@ -0,0 +1,64 @@ +package avm + +import ( + "DNA/vm/avm/utils" + "io" + "DNA/vm/avm/types" +) + +type ExecutionContext struct { + Code []byte + OpReader *utils.VmReader + PushOnly bool + BreakPoints []uint + InstructionPointer int + CodeHash []byte + engine *ExecutionEngine +} + +func NewExecutionContext(engine *ExecutionEngine, code []byte, pushOnly bool, breakPoints []uint) *ExecutionContext { + var executionContext ExecutionContext + executionContext.Code = code + executionContext.OpReader = utils.NewVmReader(code) + executionContext.PushOnly = pushOnly + executionContext.BreakPoints = breakPoints + executionContext.InstructionPointer = 0 + executionContext.engine = engine + return &executionContext +} + +func (ec *ExecutionContext) GetInstructionPointer() int { + return ec.OpReader.Position() +} + +func (ec *ExecutionContext) SetInstructionPointer(offset int64) { + ec.OpReader.Seek(offset, io.SeekStart) +} + +func (ec *ExecutionContext) GetCodeHash() []byte { + if ec.CodeHash == nil { + ec.CodeHash = ec.engine.crypto.Hash160(ec.Code) + } + return ec.CodeHash +} + +func (ec *ExecutionContext) NextInstruction() OpCode { + return OpCode(ec.Code[ec.OpReader.Position()]) +} + +func (ec *ExecutionContext) Clone() *ExecutionContext { + executionContext := NewExecutionContext(ec.engine, ec.Code, ec.PushOnly, ec.BreakPoints) + executionContext.InstructionPointer = ec.InstructionPointer + executionContext.SetInstructionPointer(int64(ec.GetInstructionPointer())) + return executionContext +} + +func (ec *ExecutionContext) GetStackItem() types.StackItemInterface { + return nil +} + +func (ec *ExecutionContext) GetExecutionContext() *ExecutionContext { + return ec +} + + diff --git a/vm/avm/execution_engine.go b/vm/avm/execution_engine.go new file mode 100644 index 00000000..c44a22bd --- /dev/null +++ b/vm/avm/execution_engine.go @@ -0,0 +1,352 @@ +package avm + +import ( + "DNA/vm/avm/interfaces" + "io" + _ "math/big" + _ "sort" + . "DNA/vm/avm/errors" + "DNA/common" +) + +const ( + ratio = 100000 + gasFree = 10 * 100000000; +) + +func NewExecutionEngine(container interfaces.ICodeContainer, crypto interfaces.ICrypto, table interfaces.ICodeTable, service IInteropService, gas common.Fixed64) *ExecutionEngine { + var engine ExecutionEngine + + engine.crypto = crypto + engine.table = table + + engine.codeContainer = container + engine.invocationStack = NewRandAccessStack() + engine.opCount = 0 + + engine.evaluationStack = NewRandAccessStack() + engine.altStack = NewRandAccessStack() + engine.state = BREAK + + engine.context = nil + engine.opCode = 0 + + engine.service = NewInteropService() + + if service != nil { + engine.service.MergeMap(service.GetServiceMap()) + } + engine.gas = gasFree + gas.GetData() + return &engine +} + +type ExecutionEngine struct { + crypto interfaces.ICrypto + table interfaces.ICodeTable + service *InteropService + + codeContainer interfaces.ICodeContainer + invocationStack *RandomAccessStack + opCount int + + evaluationStack *RandomAccessStack + altStack *RandomAccessStack + state VMState + + context *ExecutionContext + + //current opcode + opCode OpCode + gas int64 +} + +func (e *ExecutionEngine) Create(caller common.Uint160, code []byte) ([]byte, error) { + return code, nil +} + +func (e *ExecutionEngine) Call(caller common.Uint160, codeHash common.Uint160, input []byte) ([]byte, error) { + e.LoadCode(input, false) + err := e.Execute() + if err != nil { + return nil, err + } + return nil, nil +} + +func (e *ExecutionEngine) GetCodeContainer() interfaces.ICodeContainer { + return e.codeContainer +} + +func (e *ExecutionEngine) GetState() VMState { + return e.state +} + +func (e *ExecutionEngine) GetEvaluationStack() *RandomAccessStack { + return e.evaluationStack +} + +func (e *ExecutionEngine) GetEvaluationStackCount() int { + return e.evaluationStack.Count() +} + +func (e *ExecutionEngine) GetExecuteResult() bool { + return e.evaluationStack.Pop().GetStackItem().GetBoolean() +} + +func (e *ExecutionEngine) ExecutingCode() []byte { + context := e.invocationStack.Peek(0).GetExecutionContext() + if context != nil { + return context.Code + } + return nil +} + +func (e *ExecutionEngine) CurrentContext() *ExecutionContext { + context := e.invocationStack.Peek(0).GetExecutionContext() + if context != nil { + return context + } + return nil +} + +func (e *ExecutionEngine) CallingContext() *ExecutionContext { + context := e.invocationStack.Peek(1).GetExecutionContext() + if context != nil { + return context + } + return nil +} + +func (e *ExecutionEngine) EntryContext() *ExecutionContext { + context := e.invocationStack.Peek(e.invocationStack.Count() - 1).GetExecutionContext() + if context != nil { + return context + } + return nil +} + +func (e *ExecutionEngine) LoadCode(script []byte, pushOnly bool) { + e.invocationStack.Push(NewExecutionContext(e, script, pushOnly, nil)) +} + +func (e *ExecutionEngine) Execute() error { + e.state = e.state & (^BREAK) + for { + if e.state == FAULT || e.state == HALT || e.state == BREAK { + break + } + err := e.StepInto() + if err != nil { + return err + } + } + return nil +} + +func (e *ExecutionEngine) StepInto() error { + if e.invocationStack.Count() == 0 { + e.state = HALT + return nil + } + context := e.CurrentContext() + + var opCode OpCode + + if context.GetInstructionPointer() >= len(context.Code) { + opCode = RET + } else { + o, err := context.OpReader.ReadByte() + if err == io.EOF { + e.state = FAULT + return err + } + opCode = OpCode(o) + } + e.opCode = opCode + e.context = context + if !e.checkStackSize() { + return ErrOverLimitStack + } + //e.gas -= e.getPrice() * ratio + //if e.gas < 0 { + // return ErrOutOfGas + //} + state, err := e.ExecuteOp() + + if state == HALT || state == FAULT { + e.state = state + return err + } + for _, v := range context.BreakPoints { + if v == uint(context.InstructionPointer) { + e.state = HALT + return nil + } + } + return nil +} + +func (e *ExecutionEngine) ExecuteOp() (VMState, error) { + if e.opCode > PUSH16 && e.opCode != RET && e.context.PushOnly { + return FAULT, ErrBadValue + } + + if e.opCode >= PUSHBYTES1 && e.opCode <= PUSHBYTES75 { + PushData(e, e.context.OpReader.ReadBytes(int(e.opCode))) + return NONE, nil + } + + opExec := OpExecList[e.opCode] + if opExec.Exec == nil { + return FAULT, ErrNotSupportOpCode + } + if opExec.Validator != nil { + if err := opExec.Validator(e); err != nil { + return FAULT, err + } + } + return opExec.Exec(e) +} + +func (e *ExecutionEngine) StepOut() { + e.state = e.state & (^BREAK) + c := e.invocationStack.Count() + for { + if e.state == FAULT || e.state == HALT || e.state == BREAK || e.invocationStack.Count() >= c { + break + } + e.StepInto() + } +} + +func (e *ExecutionEngine) StepOver() { + if e.state == FAULT || e.state == HALT { + return + } + e.state = e.state & (^BREAK) + c := e.invocationStack.Count() + for { + if e.state == FAULT || e.state == HALT || e.state == BREAK || e.invocationStack.Count() > c { + break + } + e.StepInto() + } +} + +func (e *ExecutionEngine) AddBreakPoint(position uint) { + e.context.BreakPoints = append(e.context.BreakPoints, position) +} + +func (e *ExecutionEngine) RemoveBreakPoint(position uint) bool { + if e.invocationStack.Count() == 0 { + return false + } + bs := make([]uint, 0) + breakPoints := e.context.BreakPoints + for _, v := range breakPoints { + if v != position { + bs = append(bs, v) + } + } + e.context.BreakPoints = bs + return true +} + +func (e *ExecutionEngine) checkStackSize() bool { + size := 0 + if e.opCode < PUSH16 { + size = 1 + } else { + switch e.opCode { + case DEPTH, DUP, OVER, TUCK: + size = 1 + case UNPACK: + item := Peek(e) + if item == nil { + return false + } + size = len(item.GetStackItem().GetArray()) + } + } + size += e.evaluationStack.Count() + e.altStack.Count() + if uint32(size) > StackLimit { + return false + } + return true +} + +func (e *ExecutionEngine) getPrice() int64 { + switch e.opCode { + case NOP: + return 0 + case APPCALL, TAILCALL: + return 10 + case SYSCALL: + return e.getPriceForSysCall() + case SHA1, SHA256: + return 10 + case HASH160, HASH256: + return 20 + case CHECKSIG: + return 100 + case CHECKMULTISIG: + if e.evaluationStack.Count() == 0 { + return 1 + } + n := Peek(e).GetStackItem().GetBigInteger().Int64() + if n < 1 { + return 1 + } + return int64(100 * n) + default: + return 1 + } +} + +func (e *ExecutionEngine) getPriceForSysCall() int64 { + context := e.context + i := context.GetInstructionPointer() - 1 + c := len(context.Code) + if i >= c - 3 { + return 1 + } + l := int(context.Code[i+1]) + if i >= c - l - 2 { + return 1 + } + name := string(context.Code[i+2:l]) + switch name { + case "Neo.Blockchain.GetHeader": + return 100 + case "Neo.Blockchain.GetBlock": + return 200 + case "Neo.Blockchain.GetTransaction": + return 100 + case "Neo.Blockchain.GetAccount": + return 100 + case "Neo.Blockchain.RegisterValidator": + return 1000 * 100000000 / ratio; + case "Neo.Blockchain.GetValidators": + return 200 + case "Neo.Blockchain.CreateAsset": + return 5000 * 100000000 / ratio + case "Neo.Blockchain.GetAsset": + return 100 + case "Neo.Blockchain.CreateContract": + return 500 * 100000000 / ratio + case "Neo.Blockchain.GetContract": + return 100 + case "Neo.Transaction.GetReferences": + return 200 + case "Neo.Asset.Renew": + return Peek(e).GetStackItem().GetBigInteger().Int64() * 5000 * 100000000 / ratio + case "Neo.Storage.Get": + return 100 + case "Neo.Storage.Put": + return 1000 + case "Neo.Storage.Delete": + return 100 + default: + return 1 + } +} diff --git a/vm/avm/func_arithmetic.go b/vm/avm/func_arithmetic.go new file mode 100644 index 00000000..24f17fd1 --- /dev/null +++ b/vm/avm/func_arithmetic.go @@ -0,0 +1,49 @@ +package avm + +func opBigInt(e *ExecutionEngine) (VMState, error) { + x := PopBigInt(e) + PushData(e, BigIntOp(x, e.opCode)) + return NONE, nil +} + +func opNot(e *ExecutionEngine) (VMState, error) { + x := PopBoolean(e) + PushData(e, !x) + return NONE, nil +} + +func opNz(e *ExecutionEngine) (VMState, error) { + x := PopBigInt(e) + PushData(e, BigIntComp(x, e.opCode)) + return NONE, nil +} + +func opBigIntZip(e *ExecutionEngine) (VMState, error) { + x2 := PopBigInt(e) + x1 := PopBigInt(e) + b := BigIntZip(x1, x2, e.opCode) + PushData(e, b) + return NONE, nil +} + +func opBoolZip(e *ExecutionEngine) (VMState, error) { + x2 := PopBoolean(e) + x1 := PopBoolean(e) + PushData(e, BoolZip(x1, x2, e.opCode)) + return NONE, nil +} + +func opBigIntComp(e *ExecutionEngine) (VMState, error) { + x2 := PopBigInt(e) + x1 := PopBigInt(e) + PushData(e, BigIntMultiComp(x1, x2, e.opCode)) + return NONE, nil +} + +func opWithIn(e *ExecutionEngine) (VMState, error) { + b := PopBigInt(e) + a := PopBigInt(e) + c := PopBigInt(e) + PushData(e, WithInOp(c, a, b)) + return NONE, nil +} diff --git a/vm/avm/func_arithmetic_test.go b/vm/avm/func_arithmetic_test.go new file mode 100644 index 00000000..05e33b08 --- /dev/null +++ b/vm/avm/func_arithmetic_test.go @@ -0,0 +1,340 @@ +package avm + +import ( + "testing" + "math/big" + "DNA/vm/avm/types" +) + +var ( + engine = NewExecutionEngine(nil, nil, nil, nil, 0) +) + +func TestOpBigInt(t *testing.T) { + state, err := opBigInt(engine) + t.Log("state:", state, "err:", err) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(1)))) + engine.opCode = INC + + state, err = opBigInt(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("1 inc result 2, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.opCode = DEC + + state, err =opBigInt(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("2 dec result 1, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.opCode = NEGATE + state, err =opBigInt(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("1 negate result -1, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.opCode = ABS + state, err =opBigInt(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("-1 negate result 1, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) +} + +func TestOpNot(t *testing.T) { + engine := NewExecutionEngine(nil, nil, nil, nil, 0) + state, err := opNot(engine) + t.Log("state:", state, "err:", err) + + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(false))) + engine.opCode = NOT + state, err =opNot(engine) + if err != nil { + t.Fatal(err) + } + + t.Log("false not result true, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) + +} + +func TestOpNz(t *testing.T) { + state, err := opNz(engine) + t.Log("state:", state, "err:", err) + + if err != nil { + t.Fatal(err) + } + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(1)))) + engine.opCode = NZ + state, err = opNz(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("1 nz result true, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(0)))) + state, err = opNz(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("0 nz result false, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) + +} + +func TestBigIntZip(t *testing.T) { + state, err := opBigIntZip(engine) + t.Log("state:", state, "err:", err) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(1)))) + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(2)))) + + engine.opCode = ADD + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("1 add 2 result 3, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(1)))) + + engine.opCode = SUB + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("3 sub 1 result 2, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(2)))) + + engine.opCode = MUL + + state, err =opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("2 mul 2 result 4, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(2)))) + + engine.opCode = DIV + + state, err =opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("4 mul 2 result 2, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(3)))) + + engine.opCode = MOD + + state, err =opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("2 mod 3 result 2, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(2)))) + + engine.opCode = SHL + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("2 shl 2 result 8, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(1)))) + + engine.opCode = SHR + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("8 shr 1 result 4, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(6)))) + + engine.opCode = MIN + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("4 min 6 result 4, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(6)))) + + engine.opCode = MAX + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("4 max 6 result 6, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(4)))) + + engine.opCode = AND + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("4 and 6 result 4, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(6)))) + + engine.opCode = OR + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("4 or 6 result 6, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(4)))) + + engine.opCode = XOR + + state, err = opBigIntZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("4 xor 6 result 2, execute result:", engine.evaluationStack.Peek(0).GetStackItem().GetBigInteger()) +} + +func TestOpBoolZip(t *testing.T) { + + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(false))) + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(true))) + + engine.opCode = BOOLAND + + _, err := opBoolZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("false booland true result false, execute result", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) + + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(true))) + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(true))) + + + engine.opCode = BOOLAND + + _, err = opBoolZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("true booland true result true, execute result", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) + + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(false))) + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(false))) + + + engine.opCode = BOOLOR + + _, err = opBoolZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("false boolor false result false, execute result", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) + + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(true))) + engine.evaluationStack.Push(NewStackItem(types.NewBoolean(false))) + + + engine.opCode = BOOLOR + + _, err = opBoolZip(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("true boolor false result true, execute result", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) +} + + +func TestOpOpWithIn(t *testing.T) { + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(4)))) + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(2)))) + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(5)))) + + engine.opCode = WITHIN + + _, err := opWithIn(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("4 >= 2 && 4 < 5 result true, execute result", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) + + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(1)))) + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(2)))) + engine.evaluationStack.Push(NewStackItem(types.NewInteger(big.NewInt(3)))) + + engine.opCode = WITHIN + + _, err = opWithIn(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("1 >= 2 && 1 < 3 result false, execute result", engine.evaluationStack.Peek(0).GetStackItem().GetBoolean()) +} + diff --git a/vm/avm/func_array.go b/vm/avm/func_array.go new file mode 100644 index 00000000..e08f053d --- /dev/null +++ b/vm/avm/func_array.go @@ -0,0 +1,69 @@ +package avm + +import ( + "DNA/vm/avm/types" +) + +func opArraySize(e *ExecutionEngine) (VMState, error) { + item := PopStackItem(e) + if _, ok := item.(*types.Array); ok{ + PushData(e, len(item.GetArray())) + }else { + PushData(e, len(item.GetByteArray())) + } + + return NONE, nil +} + +func opPack(e *ExecutionEngine) (VMState, error) { + size := PopInt(e) + items := NewStackItems() + for i := 0; i < size; i++ { + items = append(items, PopStackItem(e)) + } + PushData(e, items) + return NONE, nil +} + +func opUnpack(e *ExecutionEngine) (VMState, error) { + arr := PopArray(e) + l := len(arr) + for i := l - 1; i >= 0; i-- { + Push(e, NewStackItem(arr[i])) + } + PushData(e, l) + return NONE, nil +} + +func opPickItem(e *ExecutionEngine) (VMState, error) { + index := PopInt(e) + items := PopArray(e) + PushData(e, items[index]) + return NONE, nil +} + +func opSetItem(e *ExecutionEngine) (VMState, error) { + newItem := PopStackItem(e) + index := PopInt(e) + itemArr := PopStackItem(e) + if _, ok := itemArr.(*types.Array); ok { + items := itemArr.GetArray() + items[index] = newItem + }else { + items := itemArr.GetByteArray() + items[index] = newItem.GetByteArray()[0] + } + return NONE, nil +} + +func opNewArray(e *ExecutionEngine) (VMState, error) { + count := PopInt(e) + items := NewStackItems(); + for i := 0; i < count; i++ { + items = append(items, types.NewBoolean(false)) + } + PushData(e, items) + return NONE, nil +} + + diff --git a/vm/avm/func_array_test.go b/vm/avm/func_array_test.go new file mode 100644 index 00000000..621abd49 --- /dev/null +++ b/vm/avm/func_array_test.go @@ -0,0 +1,81 @@ +package avm + +import ( + "testing" + "math/big" + "DNA/vm/avm/types" +) + + +func TestOpArraySize(t *testing.T) { + engine.opCode = ARRAYSIZE + + bs := []byte{0x51, 0x52} + i := big.NewInt(1) + + is := []types.StackItemInterface{types.NewByteArray(bs), types.NewInteger(i)} + PushData(engine, is); + + _, err := opArraySize(engine) + + if err != nil { + t.Fatal(err) + } + + t.Log("op array size result 2, execute result:", engine.GetEvaluationStack().Peek(0).GetStackItem().GetBigInteger()) +} + +func TestOpPack(t *testing.T) { + engine.opCode = PACK + + bs := []byte{0x51, 0x52} + i := big.NewInt(1) + n := 2 + + PushData(engine, bs) + + PushData(engine, i) + + PushData(engine, n) + + if _, err := opPack(engine); err != nil { + t.Fatal(err) + } + array := engine.GetEvaluationStack().Peek(0).GetStackItem().GetArray() + + for _, v := range array { + t.Log("value:", v.GetByteArray()) + } +} + +func TestOpUnPack(t *testing.T) { + engine.opCode = UNPACK + + if _, err := opUnpack(engine); err != nil { + t.Fatal(err) + } + t.Log(engine.GetEvaluationStack().Pop().GetStackItem().GetBigInteger()) + t.Log(engine.GetEvaluationStack().Pop().GetStackItem().GetBigInteger()) + t.Log(engine.GetEvaluationStack().Pop().GetStackItem().GetByteArray()) + +} + +func TestOpPickItem(t *testing.T) { + engine.opCode = PICKITEM + + bs := []byte{0x51, 0x52} + i := big.NewInt(1) + + is := []types.StackItemInterface{types.NewByteArray(bs), types.NewInteger(i)} + PushData(engine, is) + + PushData(engine, 0) + + if _, err := opPickItem(engine); err != nil { + t.Fatal(err) + } + t.Log(engine.GetEvaluationStack().Pop().GetStackItem().GetByteArray()) + +} + + diff --git a/vm/avm/func_bitwise.go b/vm/avm/func_bitwise.go new file mode 100644 index 00000000..26302168 --- /dev/null +++ b/vm/avm/func_bitwise.go @@ -0,0 +1,14 @@ +package avm + +func opInvert(e *ExecutionEngine) (VMState, error) { + i := PopBigInt(e) + PushData(e, i.Not(i)) + return NONE, nil +} + +func opEqual(e *ExecutionEngine) (VMState, error) { + b1 := PopStackItem(e) + b2 := PopStackItem(e) + PushData(e, b1.Equals(b2)) + return NONE, nil +} diff --git a/vm/avm/func_crypto.go b/vm/avm/func_crypto.go new file mode 100644 index 00000000..389c61e8 --- /dev/null +++ b/vm/avm/func_crypto.go @@ -0,0 +1,55 @@ +package avm + + +func opHash(e *ExecutionEngine) (VMState, error) { + x := PopByteArray(e) + PushData(e, Hash(x, e)) + return NONE, nil +} + +func opCheckSig(e *ExecutionEngine) (VMState, error) { + pubkey := PopByteArray(e) + signature := PopByteArray(e) + ver, err := e.crypto.VerifySignature(e.codeContainer.GetMessage(), signature, pubkey) + if err != nil { return FAULT, err } + PushData(e, ver) + return NONE, nil +} + +func opCheckMultiSig(e *ExecutionEngine) (VMState, error) { + n := PopInt(e) + if n < 1 { return FAULT, nil } + if Count(e) < n+2 { + return FAULT, nil + } + e.opCount += n + + pubkeys := make([][]byte, n) + for i := 0; i < n; i++ { + pubkeys[i] = PopByteArray(e) + } + + m := PopInt(e) + if m < 1 || m > n { return FAULT, nil } + + signatures := make([][]byte, m) + for i := 0; i < m; i++ { + signatures[i] = PopByteArray(e) + } + + message := e.codeContainer.GetMessage() + fSuccess := true + + for i, j := 0, 0; fSuccess && i < m && j < n; { + ver, _ := e.crypto.VerifySignature(message, signatures[i], pubkeys[j]) + if ver { + i++ + } + j++ + if m-i > n-j { + fSuccess = false + } + } + PushData(e, fSuccess) + return NONE, nil +} diff --git a/vm/avm/func_flowcontrol.go b/vm/avm/func_flowcontrol.go new file mode 100644 index 00000000..e05dbb9f --- /dev/null +++ b/vm/avm/func_flowcontrol.go @@ -0,0 +1,75 @@ +package avm + +import ( + . "DNA/vm/avm/errors" + "DNA/common/log" +) + +func opNop(e *ExecutionEngine) (VMState, error) { + return NONE, nil +} + +func opJmp(e *ExecutionEngine) (VMState, error) { + offset := int(e.context.OpReader.ReadInt16()) + + offset = e.context.GetInstructionPointer() + offset - 3 + + if offset > len(e.context.Code) { + return FAULT, ErrFault + } + var ( + fValue = true + ) + if e.opCode > JMP { + fValue = PopBoolean(e) + + if e.opCode == JMPIFNOT { + fValue = !fValue + } + } + if fValue { + e.context.SetInstructionPointer(int64(offset)) + } + return NONE, nil +} + +func opCall(e *ExecutionEngine) (VMState, error) { + e.invocationStack.Push(e.context.Clone()) + e.context.SetInstructionPointer(int64(e.context.GetInstructionPointer() + 2)) + e.opCode = JMP + e.context = e.CurrentContext() + opJmp(e) + return NONE, nil +} + +func opRet(e *ExecutionEngine) (VMState, error) { + e.invocationStack.Pop() + return NONE, nil +} + +func opAppCall(e *ExecutionEngine) (VMState, error) { + codeHash := e.context.OpReader.ReadBytes(20) + code, err := e.table.GetCode(codeHash) + if code == nil { + return FAULT, err + } + if e.opCode == TAILCALL { + e.invocationStack.Pop() + } + e.LoadCode(code, false) + return NONE, nil +} + +func opSysCall(e *ExecutionEngine) (VMState, error) { + s := e.context.OpReader.ReadVarString() + + log.Error("[opSysCall] service name:", s) + + success, err := e.service.Invoke(s, e) + if success { + return NONE, nil + } else { + return FAULT, err + } +} + diff --git a/vm/avm/func_pushdata.go b/vm/avm/func_pushdata.go new file mode 100644 index 00000000..c443ac5a --- /dev/null +++ b/vm/avm/func_pushdata.go @@ -0,0 +1,30 @@ +package avm + +func opPushData(e *ExecutionEngine) (VMState, error) { + data := getPushData(e) + PushData(e, data) + return NONE, nil +} + +func getPushData(e *ExecutionEngine) interface{} { + var data interface{} + if e.opCode >= PUSHBYTES1 && e.opCode <= PUSHBYTES75 { + data = e.context.OpReader.ReadBytes(int(e.opCode)) + } + switch e.opCode { + case PUSH0: + data = []byte{} + case PUSHDATA1: + d, _ := e.context.OpReader.ReadByte() + data = e.context.OpReader.ReadBytes(int(d)) + case PUSHDATA2: + data = e.context.OpReader.ReadBytes(int(e.context.OpReader.ReadUint16())) + case PUSHDATA4: + i := int(e.context.OpReader.ReadInt32()) + data = e.context.OpReader.ReadBytes(i) + case PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16: + data = int8(e.opCode - PUSH1 + 1) + } + + return data +} diff --git a/vm/avm/func_splice.go b/vm/avm/func_splice.go new file mode 100644 index 00000000..f91787ab --- /dev/null +++ b/vm/avm/func_splice.go @@ -0,0 +1,40 @@ +package avm + +func opCat(e *ExecutionEngine) (VMState, error) { + b2 := PopByteArray(e) + b1 := PopByteArray(e) + r := ByteArrZip(b1, b2, CAT) + PushData(e, r) + return NONE, nil +} + +func opSubStr(e *ExecutionEngine) (VMState, error) { + count := PopInt(e) + index := PopInt(e) + arr := PopByteArray(e) + b := arr[index: index + count] + PushData(e, b) + return NONE, nil +} + +func opLeft(e *ExecutionEngine) (VMState, error) { + count := PopInt(e) + s := PopByteArray(e) + b := s[:count] + PushData(e, b) + return NONE, nil +} + +func opRight(e *ExecutionEngine) (VMState, error) { + count := PopInt(e) + arr := PopByteArray(e) + b := arr[len(arr)-count:] + PushData(e, b) + return NONE, nil +} + +func opSize(e *ExecutionEngine) (VMState, error) { + x := Peek(e).GetStackItem() + PushData(e, len(x.GetByteArray())) + return NONE, nil +} diff --git a/vm/avm/func_stack.go b/vm/avm/func_stack.go new file mode 100644 index 00000000..cc403184 --- /dev/null +++ b/vm/avm/func_stack.go @@ -0,0 +1,101 @@ +package avm + +func opToAltStack(e *ExecutionEngine) (VMState, error) { + e.altStack.Push(Pop(e)) + return NONE, nil +} + +func opFromAltStack(e *ExecutionEngine) (VMState, error) { + Push(e, e.altStack.Pop()) + return NONE, nil +} + +func opXDrop(e *ExecutionEngine) (VMState, error) { + n := PopInt(e) + e.evaluationStack.Remove(n) + return NONE, nil +} + +func opXSwap(e *ExecutionEngine) (VMState, error) { + n := PopInt(e) + if n == 0 { return NONE, nil } + e.evaluationStack.Swap(0, n) + return NONE, nil +} + +func opXTuck(e *ExecutionEngine) (VMState, error) { + n := PopInt(e) + e.evaluationStack.Insert(n, Peek(e)) + return NONE, nil +} + +func opDepth(e *ExecutionEngine) (VMState, error) { + PushData(e, Count(e)) + return NONE, nil +} + +func opDrop(e *ExecutionEngine) (VMState, error) { + Pop(e) + return NONE, nil +} + +func opDup(e *ExecutionEngine) (VMState, error) { + Push(e, Peek(e)) + return NONE, nil +} + +func opNip(e *ExecutionEngine) (VMState, error) { + x2 := Pop(e) + Pop(e) + Push(e, x2) + return NONE, nil +} + +func opOver(e *ExecutionEngine) (VMState, error) { + x2 := Pop(e) + x1 := Peek(e) + Push(e, x2) + Push(e, x1) + return NONE, nil +} + +func opPick(e *ExecutionEngine) (VMState, error) { + n := PopInt(e) + Push(e, e.evaluationStack.Peek(n)) + return NONE, nil +} + +func opRoll(e *ExecutionEngine) (VMState, error) { + n := PopInt(e) + if n == 0 { return NONE, nil } + Push(e, e.evaluationStack.Remove(n)) + return NONE, nil +} + +func opRot(e *ExecutionEngine) (VMState, error) { + x3 := Pop(e) + x2 := Pop(e) + x1 := Pop(e) + Push(e, x2) + Push(e, x3) + Push(e, x1) + return NONE, nil +} + +func opSwap(e *ExecutionEngine) (VMState, error) { + x2 := Pop(e) + x1 := Pop(e) + Push(e, x2) + Push(e, x1) + return NONE, nil +} + +func opTuck(e *ExecutionEngine) (VMState, error) { + x2 := Pop(e) + x1 := Pop(e) + Push(e, x2) + Push(e, x1) + Push(e, x2) + return NONE, nil +} + diff --git a/vm/avm/func_validate.go b/vm/avm/func_validate.go new file mode 100644 index 00000000..bf204236 --- /dev/null +++ b/vm/avm/func_validate.go @@ -0,0 +1,257 @@ +package avm + +import ( + . "DNA/vm/avm/errors" + "bytes" + "encoding/binary" + "DNA/vm/avm/types" + "fmt" +) + +func validatorPushData4(e *ExecutionEngine) error { + index := e.context.GetInstructionPointer() + if index + 4 >= len(e.context.Code) { + return ErrOverCodeLen + } + bytesBuffer := bytes.NewBuffer(e.context.Code[index: index + 4]) + var l uint32 + binary.Read(bytesBuffer, binary.LittleEndian, &l) + if l > MaxItemSize { + return ErrOverMaxItemSize + } + return nil +} + +func validateCall(e *ExecutionEngine) error { + if err := validateInvocationStack(e); err != nil { + return err + } + return nil +} + +func validateInvocationStack(e *ExecutionEngine) error { + if uint32(e.invocationStack.Count()) > MaxStackSize { + return ErrOverStackLen + } + return nil +} + +func validateAppCall(e *ExecutionEngine) error { + if err := validateInvocationStack(e); err != nil { + return err + } + if e.table == nil { + return ErrTableIsNil + } + return nil +} + +func validateSysCall(e *ExecutionEngine) error { + if e.service == nil { + return ErrServiceIsNil + } + return nil +} + +func validateOpStack(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 1 { + return ErrUnderStackLen + } + index := PeekNInt(0, e) + if index < 0 { + return ErrBadValue + } + + return nil +} + +func validateXDrop(e *ExecutionEngine) error { + if err := validateOpStack(e); err != nil { + return err + } + return nil +} + +func validateXSwap(e *ExecutionEngine) error { + if err := validateOpStack(e); err != nil { + return err + } + return nil +} + +func validateXTuck(e *ExecutionEngine) error { + if err := validateOpStack(e); err != nil { + return err + } + return nil +} + +func validatePick(e *ExecutionEngine) error { + if err := validateOpStack(e); err != nil { + return err + } + return nil +} + +func validateRoll(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 1 { + return ErrUnderStackLen + } + index := PeekNInt(0, e) + if index < 0 { + return ErrBadValue + } + return nil +} + +func validateCat(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 2 { + return ErrUnderStackLen + } + l := len(PeekNByteArray(0, e)) + len(PeekNByteArray(1, e)) + if uint32(l) > MaxItemSize { + return ErrOverMaxItemSize + } + return nil +} + +func validateSubStr(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 3 { + return ErrUnderStackLen + } + count := PeekNInt(0, e) + if count < 0 { + return ErrBadValue + } + index := PeekNInt(1, e) + if index < 0 { + return ErrBadValue + } + arr := PeekNByteArray(2, e) + if len(arr) < index + count { + return ErrOverMaxArraySize + } + return nil +} + +func validateLeft(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 2 { + return ErrUnderStackLen + } + count := PeekNInt(0, e) + if count < 0 { + return ErrBadValue + } + arr := PeekNByteArray(1, e) + if len(arr) < count { + return ErrOverMaxArraySize + } + return nil +} + +func validateRight(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 2 { + return ErrUnderStackLen + } + count := PeekNInt(0, e) + if count < 0 { + return ErrBadValue + } + arr := PeekNByteArray(1, e) + if len(arr) < count { + return ErrOverMaxArraySize + } + return nil +} + +func validatorBigIntComp(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 2 { + return ErrUnderStackLen + } + return nil +} + +func validatePack(e *ExecutionEngine) error { + count := PeekInt(e) + if uint32(count) > MaxArraySize { + return ErrOverMaxArraySize + } + if count > EvaluationStackCount(e) { + return ErrOverStackLen + } + return nil +} + +func validatePickItem(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 2 { + return ErrUnderStackLen + } + index := PeekNInt(0, e) + if index < 0 { + return ErrBadValue + } + item := PeekN(1, e) + if item == nil { + return ErrBadValue + } + stackItem := item.GetStackItem() + if _, ok := stackItem.(*types.Array); !ok { + return ErrNotArray + } + if index >= len(stackItem.GetArray()) { + return ErrOverMaxArraySize + } + return nil +} + +func validatorSetItem(e *ExecutionEngine) error { + if EvaluationStackCount(e) < 3 { + return ErrUnderStackLen + } + newItem := PeekN(0, e) + if newItem == nil { + return ErrBadValue + } + fmt.Println("new item:", newItem) + index := PeekNInt(1, e) + if index < 0 { + return ErrBadValue + } + fmt.Println("index:", index) + arrItem := PeekN(2, e) + if arrItem == nil { + return ErrBadValue + } + fmt.Println("arrItem:", arrItem) + item := arrItem.GetStackItem() + if _, ok := item.(*types.Array); !ok { + if _, ok := item.(*types.ByteArray); ok { + fmt.Println("item:", item.GetByteArray()) + fmt.Println("item length:", len(item.GetByteArray())) + l := len(item.GetByteArray()) + if index >= l { + return ErrOverMaxArraySize + } + if len(newItem.GetStackItem().GetByteArray()) == 0 { + return ErrBadValue + } + } else { + return ErrBadValue + } + }else { + fmt.Println("item:", item.GetArray()) + fmt.Println("item length:", len(item.GetArray())) + if index >= len(item.GetArray()) { + return ErrOverMaxArraySize + } + } + return nil +} + +func validateNewArray(e *ExecutionEngine) error { + count := PeekInt(e) + if uint32(count) > MaxArraySize { + return ErrOverMaxArraySize + } + return nil +} diff --git a/vm/avm/interfaces/code_container.go b/vm/avm/interfaces/code_container.go new file mode 100644 index 00000000..8c206319 --- /dev/null +++ b/vm/avm/interfaces/code_container.go @@ -0,0 +1,6 @@ +package interfaces + +type ICodeContainer interface { + GetMessage() ([]byte) + IInteropInterface +} \ No newline at end of file diff --git a/vm/avm/interfaces/crypto.go b/vm/avm/interfaces/crypto.go new file mode 100644 index 00000000..ecb44c92 --- /dev/null +++ b/vm/avm/interfaces/crypto.go @@ -0,0 +1,9 @@ +package interfaces + +type ICrypto interface { + Hash160(message []byte) ([]byte) + + Hash256(message []byte) ([]byte) + + VerifySignature(message []byte,signature []byte, pubkey []byte) (bool,error) +} diff --git a/vm/avm/interfaces/icodetable.go b/vm/avm/interfaces/icodetable.go new file mode 100644 index 00000000..e4258379 --- /dev/null +++ b/vm/avm/interfaces/icodetable.go @@ -0,0 +1,5 @@ +package interfaces + +type ICodeTable interface { + GetCode(scriptHash []byte) ([]byte, error) +} diff --git a/vm/avm/interfaces/interop.go b/vm/avm/interfaces/interop.go new file mode 100644 index 00000000..a598b4fe --- /dev/null +++ b/vm/avm/interfaces/interop.go @@ -0,0 +1,5 @@ +package interfaces + +type IInteropInterface interface { + ToArray() ([]byte) +} diff --git a/vm/avm/interop_service.go b/vm/avm/interop_service.go new file mode 100644 index 00000000..4c0e9f0d --- /dev/null +++ b/vm/avm/interop_service.go @@ -0,0 +1,72 @@ +package avm + +import ( + . "DNA/vm/avm/errors" + "DNA/common/log" +) + +type IInteropService interface { + Register(method string, handler func(*ExecutionEngine) (bool, error)) bool + GetServiceMap() map[string]func(*ExecutionEngine) (bool, error) +} + +type InteropService struct { + serviceMap map[string]func(*ExecutionEngine) (bool, error) +} + +func NewInteropService() *InteropService { + var i InteropService + i.serviceMap = make(map[string]func(*ExecutionEngine) (bool, error), 0) + i.Register("System.ExecutionEngine.GetScriptContainer", i.GetCodeContainer) + i.Register("System.ExecutionEngine.GetExecutingScriptHash", i.GetExecutingCodeHash) + i.Register("System.ExecutionEngine.GetCallingScriptHash", i.GetCallingCodeHash) + i.Register("System.ExecutionEngine.GetEntryScriptHash", i.GetEntryCodeHash) + return &i +} + +func (is *InteropService) Register(methodName string, handler func(*ExecutionEngine) (bool, error)) bool { + if _, ok := is.serviceMap[methodName]; ok { + return false + } + is.serviceMap[methodName] = handler + return true +} + +func (i *InteropService) MergeMap(dictionary map[string]func(*ExecutionEngine) (bool, error)) { + for k, v := range dictionary { + if _, ok := i.serviceMap[k]; !ok { + i.serviceMap[k] = v + } + } +} + +func (i *InteropService) GetServiceMap() map[string]func(*ExecutionEngine) (bool, error) { + return i.serviceMap +} + +func (i *InteropService) Invoke(methodName string, engine *ExecutionEngine) (bool, error) { + if v, ok := i.serviceMap[methodName]; ok { + log.Error("Invoke MethodName:", methodName) + return v(engine) + } + return false, ErrNotSupportService +} + +func (i *InteropService) GetCodeContainer(engine *ExecutionEngine) (bool, error) { + PushData(engine, engine.codeContainer) + return true, nil +} + +func (i *InteropService) GetExecutingCodeHash(engine *ExecutionEngine) (bool, error) { + PushData(engine, engine.crypto.Hash160(engine.ExecutingCode())) + return true, nil +} + +func (i *InteropService) GetCallingCodeHash(engine *ExecutionEngine) (bool, error) { + PushData(engine, engine.crypto.Hash160(engine.CallingContext().GetCodeHash())) + return true, nil +} +func (i *InteropService) GetEntryCodeHash(engine *ExecutionEngine) (bool, error) { + PushData(engine, engine.crypto.Hash160(engine.EntryContext().GetCodeHash())) + return true, nil +} diff --git a/vm/avm/opcode.go b/vm/avm/opcode.go new file mode 100644 index 00000000..b9962860 --- /dev/null +++ b/vm/avm/opcode.go @@ -0,0 +1,124 @@ +package avm + +type OpCode byte + +const ( + // Constants + PUSH0 OpCode = 0x00 // An empty array of bytes is pushed onto the stack. + PUSHF OpCode = PUSH0 + PUSHBYTES1 OpCode = 0x01 // 0x01-0x4B The next opcode bytes is data to be pushed onto the stack + PUSHBYTES75 OpCode = 0x4B + PUSHDATA1 OpCode = 0x4C // The next byte contains the number of bytes to be pushed onto the stack. + PUSHDATA2 OpCode = 0x4D // The next two bytes contain the number of bytes to be pushed onto the stack. + PUSHDATA4 OpCode = 0x4E // The next four bytes contain the number of bytes to be pushed onto the stack. + PUSHM1 OpCode = 0x4F // The number -1 is pushed onto the stack. + PUSH1 OpCode = 0x51 // The number 1 is pushed onto the stack. + PUSHT OpCode = PUSH1 + PUSH2 OpCode = 0x52 // The number 2 is pushed onto the stack. + PUSH3 OpCode = 0x53 // The number 3 is pushed onto the stack. + PUSH4 OpCode = 0x54 // The number 4 is pushed onto the stack. + PUSH5 OpCode = 0x55 // The number 5 is pushed onto the stack. + PUSH6 OpCode = 0x56 // The number 6 is pushed onto the stack. + PUSH7 OpCode = 0x57 // The number 7 is pushed onto the stack. + PUSH8 OpCode = 0x58 // The number 8 is pushed onto the stack. + PUSH9 OpCode = 0x59 // The number 9 is pushed onto the stack. + PUSH10 OpCode = 0x5A // The number 10 is pushed onto the stack. + PUSH11 OpCode = 0x5B // The number 11 is pushed onto the stack. + PUSH12 OpCode = 0x5C // The number 12 is pushed onto the stack. + PUSH13 OpCode = 0x5D // The number 13 is pushed onto the stack. + PUSH14 OpCode = 0x5E // The number 14 is pushed onto the stack. + PUSH15 OpCode = 0x5F // The number 15 is pushed onto the stack. + PUSH16 OpCode = 0x60 // The number 16 is pushed onto the stack. + + // Flow control + NOP OpCode = 0x61 // Does nothing. + JMP OpCode = 0x62 + JMPIF OpCode = 0x63 + JMPIFNOT OpCode = 0x64 + CALL OpCode = 0x65 + RET OpCode = 0x66 + APPCALL OpCode = 0x67 + SYSCALL OpCode = 0x68 + TAILCALL OpCode = 0x69 + + // Stack + TOALTSTACK OpCode = 0x6B // Puts the input onto the top of the alt stack. Removes it from the main stack. + FROMALTSTACK OpCode = 0x6C // Puts the input onto the top of the main stack. Removes it from the alt stack. + XDROP OpCode = 0x6D + XSWAP OpCode = 0x72 + XTUCK OpCode = 0x73 + DEPTH OpCode = 0x74 // Puts the number of stack items onto the stack. + DROP OpCode = 0x75 // Removes the top stack item. + DUP OpCode = 0x76 // Duplicates the top stack item. + NIP OpCode = 0x77 // Removes the second-to-top stack item. + OVER OpCode = 0x78 // Copies the second-to-top stack item to the top. + PICK OpCode = 0x79 // The item n back in the stack is copied to the top. + ROLL OpCode = 0x7A // The item n back in the stack is moved to the top. + ROT OpCode = 0x7B // The top three items on the stack are rotated to the left. + SWAP OpCode = 0x7C // The top two items on the stack are swapped. + TUCK OpCode = 0x7D // The item at the top of the stack is copied and inserted before the second-to-top item. + + // Splice + CAT OpCode = 0x7E // Concatenates two strings. + SUBSTR OpCode = 0x7F // Returns a section of a string. + LEFT OpCode = 0x80 // Keeps only characters left of the specified point in a string. + RIGHT OpCode = 0x81 // Keeps only characters right of the specified point in a string. + SIZE OpCode = 0x82 // Returns the length of the input string. + + // Bitwise logic + INVERT OpCode = 0x83 // Flips all of the bits in the input. + AND OpCode = 0x84 // Boolean and between each bit in the inputs. + OR OpCode = 0x85 // Boolean or between each bit in the inputs. + XOR OpCode = 0x86 // Boolean exclusive or between each bit in the inputs. + EQUAL OpCode = 0x87 // Returns 1 if the inputs are exactly equal, 0 otherwise. + //EQUALVERIFY = 0x88 // Same as EQUAL, but runs VERIFY afterward. + //RESERVED1 = 0x89 // Transaction is invalid unless occuring in an unexecuted IF branch + //RESERVED2 = 0x8A // Transaction is invalid unless occuring in an unexecuted IF branch + + // Arithmetic + // Note: Arithmetic inputs are limited to signed 32-bit integers, but may overflow their output. + INC OpCode = 0x8B // 1 is added to the input. + DEC OpCode = 0x8C // 1 is subtracted from the input. + //SAL OpCode = 0x8D // The input is multiplied by 2. + //SAR OpCode = 0x8E // The input is divided by 2. + NEGATE OpCode = 0x8F // The sign of the input is flipped. + ABS OpCode = 0x90 // The input is made positive. + NOT OpCode = 0x91 // If the input is 0 or 1, it is flipped. Otherwise the output will be 0. + NZ OpCode = 0x92 // Returns 0 if the input is 0. 1 otherwise. + ADD OpCode = 0x93 // a is added to b. + SUB OpCode = 0x94 // b is subtracted from a. + MUL OpCode = 0x95 // a is multiplied by b. + DIV OpCode = 0x96 // a is divided by b. + MOD OpCode = 0x97 // Returns the remainder after dividing a by b. + SHL OpCode = 0x98 // Shifts a left b bits, preserving sign. + SHR OpCode = 0x99 // Shifts a right b bits, preserving sign. + BOOLAND OpCode = 0x9A // If both a and b are not 0, the output is 1. Otherwise 0. + BOOLOR OpCode = 0x9B // If a or b is not 0, the output is 1. Otherwise 0. + NUMEQUAL OpCode = 0x9C // Returns 1 if the numbers are equal, 0 otherwise. + NUMNOTEQUAL OpCode = 0x9E // Returns 1 if the numbers are not equal, 0 otherwise. + LT OpCode = 0x9F // Returns 1 if a is less than b, 0 otherwise. + GT OpCode = 0xA0 // Returns 1 if a is greater than b, 0 otherwise. + LTE OpCode = 0xA1 // Returns 1 if a is less than or equal to b, 0 otherwise. + GTE OpCode = 0xA2 // Returns 1 if a is greater than or equal to b, 0 otherwise. + MIN OpCode = 0xA3 // Returns the smaller of a and b. + MAX OpCode = 0xA4 // Returns the larger of a and b. + WITHIN OpCode = 0xA5 // Returns 1 if x is within the specified range (left-inclusive), 0 otherwise. + + // Crypto + //RIPEMD160 = 0xA6 // The input is hashed using RIPEMD-160. + SHA1 OpCode = 0xA7 // The input is hashed using SHA-1. + SHA256 OpCode = 0xA8 // The input is hashed using SHA-256. + HASH160 OpCode = 0xA9 + HASH256 OpCode = 0xAA + CHECKSIG OpCode = 0xAC // The entire transaction's outputs inputs and script (from the most recently-executed CODESEPARATOR to the end) are hashed. The signature used by CHECKSIG must be a valid signature for this hash and public key. If it is 1 is returned 0 otherwise. + CHECKMULTISIG OpCode = 0xAE // For each signature and public key pair CHECKSIG is executed. If more public keys than signatures are listed some key/sig pairs can fail. All signatures need to match a public key. If all signatures are valid 1 is returned 0 otherwise. Due to a bug one extra unused value is removed from the stack. + + // Array + ARRAYSIZE OpCode = 0xC0 + PACK OpCode = 0xC1 + UNPACK OpCode = 0xC2 + PICKITEM OpCode = 0xC3 + SETITEM OpCode = 0xC4 + NEWARRAY OpCode = 0xC5 + CLONE OpCode = 0xC6 +) diff --git a/vm/avm/opcode_exec.go b/vm/avm/opcode_exec.go new file mode 100644 index 00000000..85d2073a --- /dev/null +++ b/vm/avm/opcode_exec.go @@ -0,0 +1,121 @@ +package avm + +type OpExec struct { + Opcode OpCode + Name string + Exec func(*ExecutionEngine) (VMState, error) + Validator func(*ExecutionEngine) error +} + +var ( + OpExecList = [256]OpExec{ + // control flow + PUSH0: {Opcode: PUSH0, Name: "PUSH0", Exec: opPushData,}, + PUSHBYTES1: {Opcode: PUSHBYTES1, Name: "PUSHBYTES1", Exec: opPushData}, + PUSHBYTES75: {Opcode: PUSHBYTES75, Name: "PUSHBYTES75", Exec: opPushData}, + PUSHDATA1: {Opcode: PUSHDATA1, Name: "PUSHDATA1", Exec: opPushData}, + PUSHDATA2: {Opcode: PUSHDATA2, Name: "PUSHDATA2", Exec: opPushData}, + PUSHDATA4: {Opcode: PUSHDATA4, Name: "PUSHDATA4", Exec: opPushData, Validator: validatorPushData4}, + PUSHM1: {Opcode: PUSHM1, Name: "PUSHM1", Exec: opPushData}, + PUSH1: {Opcode: PUSH1, Name: "PUSH1", Exec: opPushData}, + PUSH2: {Opcode: PUSH2, Name: "PUSH2", Exec: opPushData}, + PUSH3: {Opcode: PUSH3, Name: "PUSH3", Exec: opPushData}, + PUSH4: {Opcode: PUSH4, Name: "PUSH4", Exec: opPushData}, + PUSH5: {Opcode: PUSH5, Name: "PUSH5", Exec: opPushData}, + PUSH6: {Opcode: PUSH6, Name: "PUSH6", Exec: opPushData}, + PUSH7: {Opcode: PUSH7, Name: "PUSH7", Exec: opPushData}, + PUSH8: {Opcode: PUSH8, Name: "PUSH8", Exec: opPushData}, + PUSH9: {Opcode: PUSH9, Name: "PUSH9", Exec: opPushData}, + PUSH10: {Opcode: PUSH10, Name: "PUSH10", Exec: opPushData}, + PUSH11: {Opcode: PUSH11, Name: "PUSH11", Exec: opPushData}, + PUSH12: {Opcode: PUSH12, Name: "PUSH12", Exec: opPushData}, + PUSH13: {Opcode: PUSH13, Name: "PUSH13", Exec: opPushData}, + PUSH14: {Opcode: PUSH14, Name: "PUSH14", Exec: opPushData}, + PUSH15: {Opcode: PUSH15, Name: "PUSH15", Exec: opPushData}, + PUSH16: {Opcode: PUSH16, Name: "PUSH16", Exec: opPushData}, + + //Control + NOP: {Opcode: NOP, Name: "NOP", Exec: opNop}, + JMP: {Opcode: JMP, Name: "JMP", Exec: opJmp}, + JMPIF: {Opcode: JMPIF, Name: "JMPIF", Exec: opJmp}, + JMPIFNOT: {Opcode: JMPIFNOT, Name: "JMPIFNOT", Exec: opJmp}, + CALL: {Opcode: CALL, Name: "CALL", Exec: opCall, Validator: validateCall}, + RET: {Opcode: RET, Name: "RET", Exec: opRet}, + APPCALL: {Opcode: APPCALL, Name: "APPCALL", Exec: opAppCall, Validator: validateAppCall}, + TAILCALL: {Opcode: TAILCALL, Name: "TAILCALL", Exec: opAppCall}, + SYSCALL: {Opcode: SYSCALL, Name: "SYSCALL", Exec: opSysCall, Validator: validateSysCall}, + + //Stack ops + TOALTSTACK: {Opcode: TOALTSTACK, Name: "TOALTSTACK", Exec: opToAltStack}, + FROMALTSTACK: {Opcode: FROMALTSTACK, Name: "FROMALTSTACK", Exec: opFromAltStack}, + XDROP: {Opcode: XDROP, Name: "XDROP", Exec: opXDrop, Validator: validateXDrop}, + XSWAP: {Opcode: XSWAP, Name: "XSWAP", Exec: opXSwap, Validator: validateXSwap}, + XTUCK: {Opcode: XTUCK, Name: "XTUCK", Exec: opXTuck, Validator: validateXTuck}, + DEPTH: {Opcode: DEPTH, Name: "DEPTH", Exec: opDepth}, + DROP: {Opcode: DROP, Name: "DROP", Exec: opDrop}, + DUP: {Opcode: DUP, Name: "DUP", Exec: opDup}, + NIP: {Opcode: NIP, Name: "NIP", Exec: opNip}, + OVER: {Opcode: OVER, Name: "OVER", Exec: opOver}, + PICK: {Opcode: PICK, Name: "PICK", Exec: opPick, Validator: validatePick}, + ROLL: {Opcode: ROLL, Name: "ROLL", Exec: opRoll, Validator: validateRoll}, + ROT: {Opcode: ROT, Name: "ROT", Exec: opRot}, + SWAP: {Opcode: SWAP, Name: "SWAP", Exec: opSwap}, + TUCK: {Opcode: TUCK, Name: "TUCK", Exec: opTuck}, + + //Splice + CAT: {Opcode: CAT, Name: "CAT", Exec: opCat, Validator: validateCat}, + SUBSTR: {Opcode: SUBSTR, Name: "SUBSTR", Exec: opSubStr, Validator: validateSubStr}, + LEFT: {Opcode: LEFT, Name: "LEFT", Exec: opLeft, Validator: validateLeft}, + RIGHT: {Opcode: RIGHT, Name: "RIGHT", Exec: opRight, Validator: validateRight}, + SIZE: {Opcode: SIZE, Name: "SIZE", Exec: opSize}, + + //Bitwiase logic + INVERT: {Opcode: INVERT, Name: "INVERT", Exec: opInvert}, + AND: {Opcode: AND, Name: "AND", Exec: opBigIntZip}, + OR: {Opcode: OR, Name: "OR", Exec: opBigIntZip}, + XOR: {Opcode: XOR, Name: "XOR", Exec: opBigIntZip}, + EQUAL: {Opcode: EQUAL, Name: "EQUAL", Exec: opEqual}, + + //Arithmetic + INC: {Opcode: INC, Name: "INC", Exec: opBigInt}, + DEC: {Opcode: DEC, Name: "DEC", Exec: opBigInt}, + NEGATE: {Opcode: NEGATE, Name: "NEGATE", Exec: opBigInt}, + ABS: {Opcode: ABS, Name: "ABS", Exec: opBigInt}, + NOT: {Opcode: NOT, Name: "NOT", Exec: opNot}, + NZ: {Opcode: NZ, Name: "NZ", Exec: opNz}, + ADD: {Opcode: ADD, Name: "ADD", Exec: opBigIntZip}, + SUB: {Opcode: SUB, Name: "SUB", Exec: opBigIntZip}, + MUL: {Opcode: MUL, Name: "MUL", Exec: opBigIntZip}, + DIV: {Opcode: DIV, Name: "DIV", Exec: opBigIntZip}, + MOD: {Opcode: MOD, Name: "MOD", Exec: opBigIntZip}, + SHL: {Opcode: SHL, Name: "SHL", Exec: opBigIntZip}, + SHR: {Opcode: SHR, Name: "SHR", Exec: opBigIntZip}, + BOOLAND: {Opcode: BOOLAND, Name: "BOOLAND", Exec: opBoolZip}, + BOOLOR: {Opcode: BOOLOR, Name: "BOOLOR", Exec: opBoolZip}, + NUMEQUAL: {Opcode: NUMEQUAL, Name: "NUMEQUAL", Exec: opBigIntComp, Validator: validatorBigIntComp}, + NUMNOTEQUAL: {Opcode: NUMNOTEQUAL, Name: "NUMNOTEQUAL", Exec: opBigIntComp, Validator: validatorBigIntComp}, + LT: {Opcode: LT, Name: "LT", Exec: opBigIntComp}, + GT: {Opcode: GT, Name: "GT", Exec: opBigIntComp}, + LTE: {Opcode: LTE, Name: "LTE", Exec: opBigIntComp}, + GTE: {Opcode: GTE, Name: "GTE", Exec: opBigIntComp}, + MIN: {Opcode: MIN, Name: "MIN", Exec: opBigIntZip}, + MAX: {Opcode: MAX, Name: "MAX", Exec: opBigIntZip}, + WITHIN: {Opcode: WITHIN, Name: "WITHIN", Exec: opWithIn}, + + //Crypto + SHA1: {Opcode: SHA1, Name: "SHA1", Exec: opHash}, + SHA256: {Opcode: SHA256, Name: "SHA256", Exec: opHash}, + HASH160: {Opcode: HASH160, Name: "HASH160", Exec: opHash}, + HASH256: {Opcode: HASH256, Name: "HASH256", Exec: opHash}, + CHECKSIG: {Opcode: CHECKSIG, Name: "CHECKSIG", Exec: opCheckSig}, + CHECKMULTISIG: {Opcode: CHECKMULTISIG, Name: "CHECKMULTISIG", Exec: opCheckMultiSig}, + + //Array + ARRAYSIZE: {Opcode: ARRAYSIZE, Name: "ARRAYSIZE", Exec: opArraySize}, + PACK: {Opcode: PACK, Name: "PACK", Exec: opPack, Validator: validatePack}, + UNPACK: {Opcode: UNPACK, Name: "UNPACK", Exec: opUnpack}, + PICKITEM: {Opcode: PICKITEM, Name: "PICKITEM", Exec: opPickItem, Validator: validatePickItem}, + SETITEM: {Opcode: SETITEM, Name: "SETITEM", Exec: opSetItem, Validator: validatorSetItem}, + NEWARRAY: {Opcode: NEWARRAY, Name: "NEWARRAY", Exec: opNewArray, Validator: validateNewArray}, + } +) diff --git a/vm/avm/params.go b/vm/avm/params.go new file mode 100644 index 00000000..0e226676 --- /dev/null +++ b/vm/avm/params.go @@ -0,0 +1,10 @@ +package avm + +const ( + StackLimit uint32 = 2*1024 + MaxStackSize uint32 = 1024 + MaxItemSize uint32 = 1024*1024 + MaxArraySize uint32 = 1024 + +) + diff --git a/vm/avm/params_builder.go b/vm/avm/params_builder.go new file mode 100644 index 00000000..a02850b9 --- /dev/null +++ b/vm/avm/params_builder.go @@ -0,0 +1,76 @@ +package avm + +import ( + "bytes" + "encoding/binary" + "DNA/common" +) + +type ParamsBuilder struct { + buffer *bytes.Buffer +} + +func NewParamsBuilder(buffer *bytes.Buffer) *ParamsBuilder { + return &ParamsBuilder{ buffer } +} + +func (p *ParamsBuilder) Emit(op OpCode) { + p.buffer.WriteByte(byte(op)) +} + +func (p *ParamsBuilder) EmitPushBool(data bool) { + if(data) { + p.Emit(PUSHT) + return + } + p.Emit(PUSHF) +} + +func (p *ParamsBuilder) EmitPushInteger(data int64) { + if data == -1 { + p.Emit(PUSHM1) + return + } + if data == 0 { + p.Emit(PUSH0) + return + } + if data > 0 && data < 16 { + p.Emit(OpCode((int(PUSH1) - 1 + int(data)))) + return + } + buf := bytes.NewBuffer([]byte{}) + binary.Write(buf, binary.BigEndian, data) + p.EmitPushByteArray(common.ToArrayReverse(buf.Bytes())) +} + +func (p *ParamsBuilder) EmitPushByteArray(data []byte) { + l := len(data) + if l < int(PUSHBYTES75) { + p.buffer.WriteByte(byte(l)) + } else if l < 0x100 { + p.Emit(PUSHDATA1) + p.buffer.WriteByte(byte(l)) + } else if l < 0x10000 { + p.Emit(PUSHDATA2) + b := make([]byte, 2) + binary.LittleEndian.PutUint16(b, uint16(l)) + p.buffer.Write(b) + } else { + p.Emit(PUSHDATA4) + b := make([]byte, 4) + binary.LittleEndian.PutUint32(b, uint32(l)) + p.buffer.Write(b) + } + p.buffer.Write(data) +} + +func (p *ParamsBuilder) EmitPushCall(codeHash []byte) { + p.Emit(TAILCALL) + p.buffer.Write(codeHash) +} + +func (p *ParamsBuilder) ToArray() []byte { + return p.buffer.Bytes() +} + diff --git a/vm/avm/stack.go b/vm/avm/stack.go new file mode 100644 index 00000000..0367dbea --- /dev/null +++ b/vm/avm/stack.go @@ -0,0 +1,83 @@ +package avm + +import ( + "DNA/vm/avm/types" +) + +type Element interface { + GetStackItem() types.StackItemInterface + GetExecutionContext() *ExecutionContext +} + +type RandomAccessStack struct { + e []Element +} + +func NewRandAccessStack() *RandomAccessStack { + var ras RandomAccessStack + ras.e = make([]Element, 0) + return &ras +} + +func (r *RandomAccessStack) Count() int { + return len(r.e) +} + +func (r *RandomAccessStack) Insert(index int, t Element) { + if t == nil { + return + } + l := len(r.e) + if index > l { + return + } + var array []Element + index = l - index + array = append(array, r.e[:index]...) + array = append(array, t) + array = append(array, r.e[index:]...) + r.e = array +} + +func (r *RandomAccessStack) Peek(index int) Element { + l := len(r.e) + if index >= l { + return nil + } + index = l - index + return r.e[index - 1] +} + +func (r *RandomAccessStack) Remove(index int) Element { + l := len(r.e) + if index >= l { + return nil + } + index = l - index + e := r.e[index - 1] + var si []Element + si = append(r.e[:index - 1], r.e[index:]...) + r.e = si + return e +} + +func (r *RandomAccessStack) Set(index int, t Element) { + l := len(r.e) + if index >= l { + return + } + r.e[index] = t +} + +func (r *RandomAccessStack) Push(t Element) { + r.Insert(0, t) +} + +func (r *RandomAccessStack) Pop() Element { + return r.Remove(0) +} + +func (r *RandomAccessStack) Swap(i, j int) { + l := len(r.e) + r.e[l - i - 1], r.e[l - j - 1] = r.e[l - j - 1], r.e[l - i - 1] +} diff --git a/vm/avm/stackitem.go b/vm/avm/stackitem.go new file mode 100644 index 00000000..daf45fe1 --- /dev/null +++ b/vm/avm/stackitem.go @@ -0,0 +1,21 @@ +package avm + +import "DNA/vm/avm/types" + +type StackItem struct { + _object types.StackItemInterface +} + +func NewStackItem(object types.StackItemInterface) *StackItem { + var stackItem StackItem + stackItem._object = object + return &stackItem +} + +func (s *StackItem) GetStackItem() types.StackItemInterface { + return s._object +} + +func (s *StackItem) GetExecutionContext() *ExecutionContext { + return nil +} diff --git a/vm/avm/types/array.go b/vm/avm/types/array.go new file mode 100644 index 00000000..f53a7682 --- /dev/null +++ b/vm/avm/types/array.go @@ -0,0 +1,56 @@ +package types + +import ( + "math/big" + "DNA/vm/avm/interfaces" +) + +type Array struct { + _array []StackItemInterface +} + +func NewArray(value []StackItemInterface) *Array{ + var a Array + a._array = value + return &a +} + +func (a *Array) Equals(other StackItemInterface) bool{ + if _, ok := other.(*Array); !ok { + return false + } + a1 := a._array + a2 := other.GetArray() + l1 := len(a1) + l2 := len(a2) + if l1 != l2 { return false } + for i := 0; i max { + return 0 + } + fmt.Println(value) + return value +} + +func (r *VmReader) ReadVarString() string{ + bs := r.ReadVarBytes(0X7fffffc7) + return string(bs) +} + + diff --git a/vm/avm/utils/vmreader_test.go b/vm/avm/utils/vmreader_test.go new file mode 100644 index 00000000..6666807d --- /dev/null +++ b/vm/avm/utils/vmreader_test.go @@ -0,0 +1,42 @@ +package utils + +import ( + "testing" +) + +func TestExampleVmReader(t *testing.T) { + vr := NewVmReader( []byte{0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,0x10,0x11,0x12,0x13} ) + vr1 := NewVmReader( []byte{0x14,0x15,0x16,0x17,0x18,0x19,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa} ) + + t.Log( "NewVmReader() test:", vr ) + t.Log( "NewVmReader() test:", vr.BaseStream ) + + //bt := vr.ReadByte() + //t.Log( "ReadByte() test:", bt,vr.ReadByte() ) + + bb := vr.ReadBytes(4) + t.Log( "ReadBytes() test:", bb ) + + t.Log( "ReadUint16() test:", vr.ReadUint16() ) + + t.Log( "ReadUInt32() test:", vr.ReadUInt32() ) + + t.Log( "ReadUInt64() test:", vr.ReadUInt64() ) + + t.Log( "ReadInt16() test:", vr1.ReadInt16() ) + + t.Log( "ReadInt32() test:", vr1.ReadInt32() ) + + t.Log( "Position() test:", vr1.Position() ) + + t.Log( "Length() test:", vr1.Length() ) + + offset, _ := vr1.Seek( 1, 1 ) + t.Log( "Seek() test:", offset ) + + //bb1 := vr1.ReadVarInt( 999 ) + //t.Log( "ReadVarInt() test:", bb1 ) + + + // output: ok +} diff --git a/vm/avm/vm_state.go b/vm/avm/vm_state.go new file mode 100644 index 00000000..7ceb9b67 --- /dev/null +++ b/vm/avm/vm_state.go @@ -0,0 +1,12 @@ +package avm + +type VMState byte + +const ( + NONE VMState = 0 + HALT VMState = 1 << 0 + FAULT VMState = 1 << 1 + BREAK VMState = 1 << 2 + + INSUFFICIENT_RESOURCE VMState = 1 << 4 +) diff --git a/vm/common_test.go b/vm/common_test.go deleted file mode 100644 index 069715c0..00000000 --- a/vm/common_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package vm - -import ( - "math/big" - "testing" -) - -func TestCommon(t *testing.T) { - i := ToBigInt(big.NewInt(1)) - t.Log("i", i) -} diff --git a/vm/errors/errors.go b/vm/errors/errors.go deleted file mode 100755 index b7ffe75f..00000000 --- a/vm/errors/errors.go +++ /dev/null @@ -1,11 +0,0 @@ -package errors - - -import "errors" - -var ( - ErrBadValue = errors.New("bad value") - ErrBadType = errors.New("bad type") - ErrOverLen = errors.New("the count over the size") - ErrFault = errors.New("The exeution meet fault") -) diff --git a/vm/evm/Interface.go b/vm/evm/Interface.go new file mode 100644 index 00000000..fbd771b7 --- /dev/null +++ b/vm/evm/Interface.go @@ -0,0 +1,20 @@ +package evm + +import ( + "DNA/common" + "math/big" +) + +type StateDB interface { + GetState(common.Uint160, common.Hash) common.Hash + SetState(common.Uint160, common.Hash, common.Hash) + + GetCode(common.Uint160) []byte + SetCode(common.Uint160, []byte) + GetCodeSize(common.Uint160) int + + GetBalance(common.Uint160) *big.Int + AddBalance(common.Uint160, *big.Int) + + Suicide(common.Uint160) bool +} diff --git a/vm/evm/abi/abi.go b/vm/evm/abi/abi.go new file mode 100644 index 00000000..c3c843ea --- /dev/null +++ b/vm/evm/abi/abi.go @@ -0,0 +1,357 @@ +package abi + +import ( + "fmt" + "io" + "encoding/json" + "reflect" + "strings" + "encoding/binary" + "math/big" + "DNA/common" +) + +var ( + r_interSlice = reflect.TypeOf([]interface{}{}) +) + +type ABI struct { + Constructor Method + Methods map[string]Method + Events map[string]Event +} + +func JSON(reader io.Reader) (ABI, error) { + dec := json.NewDecoder(reader) + + var abi ABI + if err := dec.Decode(&abi); err != nil { + return ABI{}, err + } + + return abi, nil +} + +func (abi *ABI) Pack(name string, args ...interface{}) ([]byte, error) { + var method Method + if name == "" { + method = abi.Constructor + } else { + m, exist := abi.Methods[name] + if !exist { + return nil, fmt.Errorf("method '%s' not found", name) + } + method = m + } + + arguments, err := method.pack(method, args...) + + if err != nil { + return nil, err + } + + if name == "" { + return arguments, nil + } + + return append(method.Id(), arguments...), nil +} + +func (abi *ABI) UnmarshalJSON(data []byte) error { + var fields []struct { + Type string + Name string + Constant bool + Indexed bool + Anonymous bool + Inputs []Argument + Outputs []Argument + } + + if err := json.Unmarshal(data, &fields); err != nil { + return err + } + + abi.Methods = make(map[string]Method) + abi.Events = make(map[string]Event) + for _, field := range fields { + switch field.Type { + case "constructor": + abi.Constructor = Method{ + Inputs: field.Inputs, + } + // empty defaults to function according to the abi spec + case "function", "": + abi.Methods[field.Name] = Method{ + Name: field.Name, + Const: field.Constant, + Inputs: field.Inputs, + Outputs: field.Outputs, + } + case "event": + abi.Events[field.Name] = Event{ + Name: field.Name, + Anonymous: field.Anonymous, + Inputs: field.Inputs, + } + } + } + + return nil +} + +func (abi ABI) Unpack(v interface{}, name string, output []byte) error { + var method = abi.Methods[name] + + if len(output) == 0 { + return fmt.Errorf("abi: unmarshalling empty output") + } + + // make sure the passed value is a pointer + valueOf := reflect.ValueOf(v) + if reflect.Ptr != valueOf.Kind() { + return fmt.Errorf("abi: Unpack(non-pointer %T)", v) + } + + var ( + value = valueOf.Elem() + typ = value.Type() + ) + + if len(method.Outputs) > 1 { + switch value.Kind() { + case reflect.Struct: + for i := 0; i < len(method.Outputs); i++ { + marshalledValue, err := toGoType(i, method.Outputs[i], output) + if err != nil { + return err + } + reflectValue := reflect.ValueOf(marshalledValue) + + for j := 0; j < typ.NumField(); j++ { + field := typ.Field(j) + if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] { + if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil { + return err + } + } + } + } + case reflect.Slice: + if !value.Type().AssignableTo(r_interSlice) { + return fmt.Errorf("abi: cannot marshal tuple in to slice %T (only []interface{} is supported)", v) + } + + if value.Len() > 0 { + if len(method.Outputs) > value.Len() { + return fmt.Errorf("abi: cannot marshal in to slices of unequal size (require: %v, got: %v)", len(method.Outputs), value.Len()) + } + + for i := 0; i < len(method.Outputs); i++ { + marshalledValue, err := toGoType(i, method.Outputs[i], output) + if err != nil { + return err + } + reflectValue := reflect.ValueOf(marshalledValue) + if err := set(value.Index(i).Elem(), reflectValue, method.Outputs[i]); err != nil { + return err + } + } + return nil + } + + z := reflect.MakeSlice(typ, 0, len(method.Outputs)) + for i := 0; i < len(method.Outputs); i++ { + marshalledValue, err := toGoType(i, method.Outputs[i], output) + if err != nil { + return err + } + z = reflect.Append(z, reflect.ValueOf(marshalledValue)) + } + value.Set(z) + default: + return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ) + } + + } else { + marshalledValue, err := toGoType(0, method.Outputs[0], output) + if err != nil { + return err + } + if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil { + return err + } + } + + return nil +} + +func toGoType(i int, t Argument, output []byte) (interface{}, error) { + // we need to treat slices differently + if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy { + return toGoSlice(i, t, output) + } + + index := i * 32 + if index+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32) + } + + var returnOutput []byte + switch t.Type.T { + case StringTy, BytesTy: + offset := int(binary.BigEndian.Uint64(output[index+24 : index+32])) + if offset+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32) + } + size := int(binary.BigEndian.Uint64(output[offset+24 : offset+32])) + if offset+32+size > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size) + } + + returnOutput = output[offset+32 : offset+32+size] + default: + returnOutput = output[index : index+32] + } + + // convert the bytes to whatever is specified by the ABI. + switch t.Type.T { + case IntTy, UintTy: + return readInteger(t.Type.Kind, returnOutput), nil + case BoolTy: + return !allZero(returnOutput), nil + case AddressTy: + return common.BytesToUint160(returnOutput), nil + case HashTy: + return common.BytesToHash(returnOutput), nil + case BytesTy, FixedBytesTy, FunctionTy: + return returnOutput, nil + case StringTy: + return string(returnOutput), nil + } + return nil, fmt.Errorf("abi: unknown type %v", t.Type.T) +} + +func allZero(b []byte) bool { + for _, byte := range b { + if byte != 0 { + return false + } + } + return true +} + +func readInteger(kind reflect.Kind, b []byte) interface{} { + switch kind { + case reflect.Uint8: + return uint8(b[len(b)-1]) + case reflect.Uint16: + return binary.BigEndian.Uint16(b[len(b)-2:]) + case reflect.Uint32: + return binary.BigEndian.Uint32(b[len(b)-4:]) + case reflect.Uint64: + return binary.BigEndian.Uint64(b[len(b)-8:]) + case reflect.Int8: + return int8(b[len(b)-1]) + case reflect.Int16: + return int16(binary.BigEndian.Uint16(b[len(b)-2:])) + case reflect.Int32: + return int32(binary.BigEndian.Uint32(b[len(b)-4:])) + case reflect.Int64: + return int64(binary.BigEndian.Uint64(b[len(b)-8:])) + default: + return new(big.Int).SetBytes(b) + } +} + +func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { + index := i * 32 + if index+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32) + } + elem := t.Type.Elem + + var refSlice reflect.Value + switch elem.T { + case IntTy, UintTy, BoolTy: + switch t.Type.Kind { + case reflect.Bool: + refSlice = reflect.ValueOf([]bool(nil)) + case reflect.Uint8: + refSlice = reflect.ValueOf([]uint8(nil)) + case reflect.Uint16: + refSlice = reflect.ValueOf([]uint16(nil)) + case reflect.Uint32: + refSlice = reflect.ValueOf([]uint32(nil)) + case reflect.Uint64: + refSlice = reflect.ValueOf([]uint64(nil)) + case reflect.Int8: + refSlice = reflect.ValueOf([]int8(nil)) + case reflect.Int16: + refSlice = reflect.ValueOf([]int16(nil)) + case reflect.Int32: + refSlice = reflect.ValueOf([]int32(nil)) + case reflect.Int64: + refSlice = reflect.ValueOf([]int64(nil)) + default: + refSlice = reflect.ValueOf([]*big.Int(nil)) + } + case AddressTy: + refSlice = reflect.ValueOf([]common.Uint160(nil)) + case HashTy: + refSlice = reflect.ValueOf([]common.Hash(nil)) + case FixedBytesTy: + refSlice = reflect.ValueOf([][]byte(nil)) + default: + return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T) + } + + var slice []byte + var size int + var offset int + if t.Type.IsSlice { + offset = int(binary.BigEndian.Uint64(output[index+24 : index+32])) + if offset+32 > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32) + } + + slice = output[offset:] + size = int(binary.BigEndian.Uint64(slice[24:32])) + slice = slice[32:] + if size*32 > len(slice) { + return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32) + } + + slice = slice[:size*32] + } else if t.Type.IsArray { + size = t.Type.SliceSize + + if index+32*size > len(output) { + return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size) + } + slice = output[index : index+size*32] + } + + for i := 0; i < size; i++ { + var ( + inter interface{} + returnOutput = slice[i*32 : i*32+32] + ) + switch elem.T { + case IntTy, UintTy: + inter = readInteger(t.Type.Kind, returnOutput) + case BoolTy: + inter = !allZero(returnOutput) + case AddressTy: + inter = common.BytesToUint160(returnOutput) + case HashTy: + inter = common.BytesToHash(returnOutput) + case FixedBytesTy: + inter = returnOutput + } + refSlice = reflect.Append(refSlice, reflect.ValueOf(inter)) + } + + return refSlice.Interface(), nil +} + diff --git a/vm/evm/abi/argument.go b/vm/evm/abi/argument.go new file mode 100644 index 00000000..c2076a38 --- /dev/null +++ b/vm/evm/abi/argument.go @@ -0,0 +1,33 @@ +package abi + +import ( + "fmt" + "encoding/json" +) + +type Argument struct { + Name string + Type Type + Indexed bool // indexed is only used by events +} + +func (a *Argument) UnmarshalJSON(data []byte) error { + var extarg struct { + Name string + Type string + Indexed bool + } + err := json.Unmarshal(data, &extarg) + if err != nil { + return fmt.Errorf("argument json err: %v", err) + } + + a.Type, err = NewType(extarg.Type) + if err != nil { + return err + } + a.Name = extarg.Name + a.Indexed = extarg.Indexed + + return nil +} \ No newline at end of file diff --git a/vm/evm/abi/error.go b/vm/evm/abi/error.go new file mode 100644 index 00000000..0bd25d95 --- /dev/null +++ b/vm/evm/abi/error.go @@ -0,0 +1,52 @@ +package abi + +import ( + "reflect" + "fmt" +) + +func formatSliceString(kind reflect.Kind, sliceSize int) string { + if sliceSize == -1 { + return fmt.Sprintf("[]%v", kind) + } + return fmt.Sprintf("[%d]%v", sliceSize, kind) +} + +func sliceTypeCheck(t Type, val reflect.Value) error { + if val.Kind() != reflect.Slice && val.Kind() != reflect.Array { + return typeErr(formatSliceString(t.Kind, t.SliceSize), val.Type()) + } + if t.IsArray && val.Len() != t.SliceSize { + return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), formatSliceString(val.Type().Elem().Kind(), val.Len())) + } + + if t.Elem.IsSlice { + if val.Len() > 0 { + return sliceTypeCheck(*t.Elem, val.Index(0)) + } + } else if t.Elem.IsArray { + return sliceTypeCheck(*t.Elem, val.Index(0)) + } + + if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind { + return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), val.Type()) + } + return nil +} + + +func typeCheck(t Type, value reflect.Value) error { + if t.IsSlice || t.IsArray { + return sliceTypeCheck(t, value) + } + fmt.Println("t.kind", t.Kind, value.Kind()) + // Check base type validity. Element types will be checked later on. + if t.Kind != value.Kind() { + return typeErr(t.Kind, value.Kind()) + } + return nil +} + +func typeErr(expected, got interface{}) error { + return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected) +} diff --git a/vm/evm/abi/event.go b/vm/evm/abi/event.go new file mode 100644 index 00000000..18df2bbd --- /dev/null +++ b/vm/evm/abi/event.go @@ -0,0 +1,7 @@ +package abi + +type Event struct { + Name string + Anonymous bool + Inputs []Argument +} \ No newline at end of file diff --git a/vm/evm/abi/method.go b/vm/evm/abi/method.go new file mode 100644 index 00000000..f6862baa --- /dev/null +++ b/vm/evm/abi/method.go @@ -0,0 +1,63 @@ +package abi + +import ( + "fmt" + "strings" + "DNA/vm/evm/crypto" + "reflect" +) + +type Method struct { + Name string + Const bool + Inputs []Argument + Outputs []Argument +} + +func (m Method) pack(method Method, args ...interface{}) ([]byte, error) { + if len(args) != len(method.Inputs) { + return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs)) + } + var variableInput []byte + + var ret []byte + + for i, a := range args { + input := method.Inputs[i] + packed, err := input.Type.pack(reflect.ValueOf(a)) + if err != nil { + return nil, fmt.Errorf("`%s` %v", method.Name, err) + } + // check for a slice type (string, bytes, slice) + if input.Type.requiresLengthPrefix() { + // calculate the offset + offset := len(method.Inputs)*32 + len(variableInput) + // set the offset + ret = append(ret, packNum(reflect.ValueOf(offset))...) + // Append the packed output to the variable input. The variable input + // will be appended at the end of the input. + variableInput = append(variableInput, packed...) + } else { + // append the packed value to the input + ret = append(ret, packed...) + } + } + // append the variable input at the end of the packed input + ret = append(ret, variableInput...) + + return ret, nil +} + +func (m Method) Sig() string { + types := make([]string, len(m.Inputs)) + i := 0 + for _, input := range m.Inputs { + types[i] = input.Type.String() + i++ + } + return fmt.Sprintf("%v(%v)", m.Name, strings.Join(types, ",")) +} + +func (m Method) Id() []byte { + return crypto.Keccak256([]byte(m.Sig()))[:4] +} diff --git a/vm/evm/abi/number.go b/vm/evm/abi/number.go new file mode 100644 index 00000000..3ad5b80a --- /dev/null +++ b/vm/evm/abi/number.go @@ -0,0 +1,31 @@ +package abi + +import ( + "reflect" + "math/big" + "DNA/vm/evm/common" + . "DNA/common" +) + +var ( + big_t = reflect.TypeOf(big.Int{}) + ubig_t = reflect.TypeOf(big.Int{}) + address_t = reflect.TypeOf(Uint160{}) +) + + +func U256(n *big.Int) []byte { + return common.PaddedBigBytes(n, 32) +} + +func packNum(value reflect.Value) []byte { + switch kind := value.Kind(); kind { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return U256(new(big.Int).SetUint64(value.Uint())) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return U256(big.NewInt(value.Int())) + case reflect.Ptr: + return U256(value.Interface().(*big.Int)) + } + return nil +} diff --git a/vm/evm/abi/packing.go b/vm/evm/abi/packing.go new file mode 100644 index 00000000..ef2eb2b3 --- /dev/null +++ b/vm/evm/abi/packing.go @@ -0,0 +1,70 @@ +package abi + +import ( + "reflect" + "DNA/vm/evm/common" +) + +func packBytesSlice(bytes []byte, l int) []byte { + len := packNum(reflect.ValueOf(l)) + return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...) +} + +func packElement(t Type, reflectValue reflect.Value) []byte { + switch t.T { + case IntTy, UintTy: + return packNum(reflectValue) + case StringTy: + return packBytesSlice([]byte(reflectValue.String()), reflectValue.Len()) + case AddressTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + + return common.LeftPadBytes(reflectValue.Bytes(), 32) + case BoolTy: + if reflectValue.Bool() { + return common.PaddedBigBytes(common.Big1, 32) + } else { + return common.PaddedBigBytes(common.Big0, 32) + } + case BytesTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()) + case FixedBytesTy, FunctionTy: + if reflectValue.Kind() == reflect.Array { + reflectValue = mustArrayToByteSlice(reflectValue) + } + return common.RightPadBytes(reflectValue.Bytes(), 32) + } + return []byte{} +} + +func reflectIntKind(unsigned bool, size int) reflect.Kind { + switch size { + case 8: + if unsigned { + return reflect.Uint8 + } + return reflect.Int8 + case 16: + if unsigned { + return reflect.Uint16 + } + return reflect.Int16 + case 32: + if unsigned { + return reflect.Uint32 + } + return reflect.Int32 + case 64: + if unsigned { + return reflect.Uint64 + } + return reflect.Int64 + } + return reflect.Ptr +} + diff --git a/vm/evm/abi/reflect.go b/vm/evm/abi/reflect.go new file mode 100644 index 00000000..249f8991 --- /dev/null +++ b/vm/evm/abi/reflect.go @@ -0,0 +1,42 @@ +package abi + +import ( + "reflect" + "fmt" +) + +func indirect(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Ptr && v.Elem().Type() != big_t { + return indirect(v.Elem()) + } + return v +} + +func mustArrayToByteSlice(value reflect.Value) reflect.Value { + slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len()) + reflect.Copy(slice, value) + return slice +} + +func set(dst, src reflect.Value, output Argument) error { + dstType := dst.Type() + srcType := src.Type() + + switch { + case dstType.AssignableTo(src.Type()): + dst.Set(src) + case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice: + if dst.Len() < output.Type.SliceSize { + return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len()) + } + reflect.Copy(dst, src) + case dstType.Kind() == reflect.Interface: + dst.Set(src) + case dstType.Kind() == reflect.Ptr: + return set(dst.Elem(), src, output) + default: + return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type()) + } + return nil +} + diff --git a/vm/evm/abi/type.go b/vm/evm/abi/type.go new file mode 100644 index 00000000..61d4ba09 --- /dev/null +++ b/vm/evm/abi/type.go @@ -0,0 +1,177 @@ +package abi + +import ( + "reflect" + "fmt" + "strconv" + "regexp" +) + +type Type struct { + IsSlice, IsArray bool + SliceSize int + + Elem *Type + + Kind reflect.Kind + Type reflect.Type + Size int + T byte // Our own type checking + + stringKind string // holds the unparsed string for deriving signatures +} + +const ( + IntTy byte = iota + UintTy + BoolTy + StringTy + SliceTy + AddressTy + FixedBytesTy + BytesTy + HashTy + FixedpointTy + FunctionTy +) + +var ( + fullTypeRegex = regexp.MustCompile(`([a-zA-Z0-9]+)(\[([0-9]*)\])?`) + typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?") +) + +func NewType(t string) (typ Type, err error) { + res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0] + // check if type is slice and parse type. + switch { + case res[3] != "": + // err is ignored. Already checked for number through the regexp + typ.SliceSize, _ = strconv.Atoi(res[3]) + typ.IsArray = true + case res[2] != "": + typ.IsSlice, typ.SliceSize = true, -1 + case res[0] == "": + return Type{}, fmt.Errorf("abi: type parse error: %s", t) + } + if typ.IsArray || typ.IsSlice { + sliceType, err := NewType(res[1]) + if err != nil { + return Type{}, err + } + typ.Elem = &sliceType + typ.stringKind = sliceType.stringKind + t[len(res[1]):] + // Although we know that this is an array, we cannot return + // as we don't know the type of the element, however, if it + // is still an array, then don't determine the type. + if typ.Elem.IsArray || typ.Elem.IsSlice { + return typ, nil + } + } + + // parse the type and size of the abi-type. + parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0] + // varSize is the size of the variable + var varSize int + if len(parsedType[3]) > 0 { + var err error + varSize, err = strconv.Atoi(parsedType[2]) + if err != nil { + return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) + } + } + // varType is the parsed abi type + varType := parsedType[1] + // substitute canonical integer + if varSize == 0 && (varType == "int" || varType == "uint") { + varSize = 256 + t += "256" + } + + // only set stringKind if not array or slice, as for those, + // the correct string type has been set + if !(typ.IsArray || typ.IsSlice) { + typ.stringKind = t + } + + switch varType { + case "int": + typ.Kind = reflectIntKind(false, varSize) + typ.Type = big_t + typ.Size = varSize + typ.T = IntTy + case "uint": + typ.Kind = reflectIntKind(true, varSize) + typ.Type = ubig_t + typ.Size = varSize + typ.T = UintTy + case "bool": + typ.Kind = reflect.Bool + typ.T = BoolTy + case "address": + typ.Kind = reflect.Array + typ.Type = address_t + typ.Size = 20 + typ.T = AddressTy + case "string": + typ.Kind = reflect.String + typ.Size = -1 + typ.T = StringTy + case "bytes": + sliceType, _ := NewType("uint8") + typ.Elem = &sliceType + if varSize == 0 { + typ.IsSlice = true + typ.T = BytesTy + typ.SliceSize = -1 + } else { + typ.IsArray = true + typ.T = FixedBytesTy + typ.SliceSize = varSize + } + case "function": + sliceType, _ := NewType("uint8") + typ.Elem = &sliceType + typ.IsArray = true + typ.T = FunctionTy + typ.SliceSize = 24 + default: + return Type{}, fmt.Errorf("unsupported arg type: %s", t) + } + + return +} + + +func (t Type) String() (out string) { + return t.stringKind +} + +func (t Type) pack(v reflect.Value) ([]byte, error) { + v = indirect(v) + if err := typeCheck(t, v); err != nil { + return nil, err + } + + if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy && t.T != FunctionTy { + var packed []byte + + for i := 0; i < v.Len(); i++ { + val, err := t.Elem.pack(v.Index(i)) + if err != nil { + return nil, err + } + packed = append(packed, val...) + } + if t.IsSlice { + return packBytesSlice(packed, v.Len()), nil + } else if t.IsArray { + return packed, nil + } + } + + return packElement(t, v), nil +} + +func (t Type) requiresLengthPrefix() bool { + return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice) +} diff --git a/vm/evm/analysis.go b/vm/evm/analysis.go new file mode 100644 index 00000000..71b37a66 --- /dev/null +++ b/vm/evm/analysis.go @@ -0,0 +1,38 @@ +package evm + +import ( + "math/big" + "DNA/common" +) + + +type destinations map[common.Uint160][]byte + +func (d destinations) has(codeHash common.Uint160, code []byte, dest *big.Int) bool { + udest := dest.Uint64() + + if dest.BitLen() >= 63 || udest >= uint64(len(code)) { + return false + } + + m, analysed := d[codeHash] + if !analysed { + m = jumpdest(code) + d[codeHash] = m + } + return (m[udest/8] & (1 << (udest % 8))) != 0 +} + +func jumpdest(code []byte) []byte { + m := make([]byte, len(code)/8+1) + for pc := uint64(0); pc < uint64(len(code)); pc++ { + var op OpCode = OpCode(code[pc]) + if op == JUMPDEST { + m[pc/8] |= 1 << (pc % 8) + } else if op >= PUSH1 && op <= PUSH32 { + a := uint64(op) - uint64(PUSH1) + 1 + pc += a + } + } + return m +} diff --git a/vm/evm/common/common.go b/vm/evm/common/common.go new file mode 100644 index 00000000..5c30b555 --- /dev/null +++ b/vm/evm/common/common.go @@ -0,0 +1,122 @@ +package common + +import ( + "math/big" +) + +var ( + Big0 = big.NewInt(0) + Big1 = big.NewInt(1) + Big32 = big.NewInt(32) + tt255 = BigPow(2, 255) + tt256 = BigPow(2, 256) + tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1)) + wordBits = 32 << (uint64(^big.Word(0)) >> 63) + wordBytes = wordBits / 8 +) + +func BigPow(a, b int64) *big.Int { + r := big.NewInt(a) + return r.Exp(r, big.NewInt(b), nil) +} + +func GetData(data []byte, start, size *big.Int) []byte { + dlen := big.NewInt(int64(len(data))) + s := BigMin(start, dlen) + e := BigMin(new(big.Int).Add(s, size), dlen) + return RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64())) +} + +func RightPadBytes(slice []byte, l int) []byte { + if l <= len(slice) { + return slice + } + + padded := make([]byte, l) + copy(padded, slice) + + return padded +} + +func LeftPadBytes(slice []byte, l int) []byte { + if l <= len(slice) { + return slice + } + + padded := make([]byte, l) + copy(padded[l-len(slice):], slice) + + return padded +} + +func BigMax(x, y *big.Int) *big.Int { + if x.Cmp(y) < 0 { + return y + } + return x +} + +func BigMin(x, y *big.Int) *big.Int { + if x.Cmp(y) > 0 { + return y + } + return x +} + +func PaddedBigBytes(bigint *big.Int, n int) []byte { + if bigint.BitLen()/8 >= n { + return bigint.Bytes() + } + ret := make([]byte, n) + ReadBits(bigint, ret) + return ret +} + +func ReadBits(bigint *big.Int, buf []byte) { + i := len(buf) + for _, d := range bigint.Bits() { + for j := 0; j < wordBytes && i > 0; j++ { + i-- + buf[i] = byte(d) + d >>= 8 + } + } +} + +func CalcMemSize(off, l *big.Int) *big.Int { + if l.Sign() == 0 { + return Big0 + } + + return new(big.Int).Add(off, l) +} + +func Exp(base, exponent *big.Int) *big.Int { + result := big.NewInt(1) + + for _, word := range exponent.Bits() { + for i := 0; i < wordBits; i++ { + if word&1 == 1 { + U256(result.Mul(result, base)) + } + U256(base.Mul(base, base)) + word >>= 1 + } + } + return result +} + +func U256(x *big.Int) *big.Int { + return x.And(x, tt256m1) +} + +func S256(x *big.Int) *big.Int { + if x.Cmp(tt255) < 0 { + return x + } else { + return new(big.Int).Sub(x, tt256) + } +} + + + diff --git a/vm/evm/contract.go b/vm/evm/contract.go new file mode 100644 index 00000000..87619044 --- /dev/null +++ b/vm/evm/contract.go @@ -0,0 +1,46 @@ +package evm + +import ( + "DNA/common" + "math/big" +) + + +type Contract struct { + Caller common.Uint160 + Code []byte + CodeHash common.Uint160 + Input []byte + value *big.Int + jumpdest destinations +} + +func NewContract(caller common.Uint160) *Contract { + var contract Contract + contract.value = new(big.Int) + contract.jumpdest = make(destinations, 0) + contract.Caller = caller + return &contract +} + +func (c *Contract) GetOp(n uint64) OpCode { + return OpCode(c.GetByte(n)) +} + +func (c *Contract) GetByte(n uint64) byte { + if n < uint64(len(c.Code)) { + return c.Code[n] + } + return 0 +} + +func (c *Contract) SetCode(code []byte, codeHash common.Uint160) { + c.Code = code + c.CodeHash = codeHash +} + +func (c *Contract) SetCallCode(code, input []byte, codeHash common.Uint160) { + c.Code = code + c.Input = input + c.CodeHash = codeHash +} diff --git a/vm/evm/contracts.go b/vm/evm/contracts.go new file mode 100644 index 00000000..1d7b2f8a --- /dev/null +++ b/vm/evm/contracts.go @@ -0,0 +1,73 @@ +package evm + +import ( + "DNA/common" + "math/big" + . "DNA/vm/evm/common" + "DNA/vm/evm/crypto" + "crypto/sha256" + "golang.org/x/crypto/ripemd160" +) + +type PrecompiledContract interface { + Run(input []byte) ([]byte, error) +} + +func allZero(b []byte) bool { + for _, byte := range b { + if byte != 0 { + return false + } + } + return true +} + +var PrecompiledContracts = map[common.Uint160]PrecompiledContract{ + common.BytesToUint160([]byte{1}): &ecrecover{}, + common.BytesToUint160([]byte{2}): &sha256hash{}, + common.BytesToUint160([]byte{3}): &ripemd160hash{}, + common.BytesToUint160([]byte{4}): &dataCopy{}, +} + +type ecrecover struct{} + +func (c *ecrecover) Run(in []byte) ([]byte, error) { + const ecRecoverInputLength = 128 + in = RightPadBytes(in, ecRecoverInputLength) + + r := new(big.Int).SetBytes(in[64:96]) + s := new(big.Int).SetBytes(in[96:128]) + v := in[63] - 27 + + if !allZero(in[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) { + return nil, nil + } + pubKey, err := crypto.Ecrecover(in[:32], append(in[64:128], v)) + if err != nil { + return nil, nil + } + + return LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil +} + +type sha256hash struct{} + +func (c *sha256hash) Run(in []byte) ([]byte, error) { + h := sha256.Sum256(in) + return h[:], nil +} + +type ripemd160hash struct{} + +func (c *ripemd160hash) Run(in []byte) ([]byte, error) { + ripemd := ripemd160.New() + ripemd.Write(in) + return LeftPadBytes(ripemd.Sum(nil), 32), nil +} + +type dataCopy struct{} +func (c *dataCopy) Run(in []byte) ([]byte, error) { + return in, nil +} + + diff --git a/vm/evm/crypto/crypto.go b/vm/evm/crypto/crypto.go new file mode 100644 index 00000000..44210161 --- /dev/null +++ b/vm/evm/crypto/crypto.go @@ -0,0 +1,38 @@ +package crypto + +import ( + "github.com/ethereum/go-ethereum/crypto/sha3" + "math/big" + "DNA/vm/evm/common" + "github.com/ethereum/go-ethereum/crypto/secp256k1" +) + +var ( + secp256k1_N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) + secp256k1_halfN = new(big.Int).Div(secp256k1_N, big.NewInt(2)) +) + +func Keccak256(data ...[]byte) []byte { + d := sha3.NewKeccak256() + for _, b := range data { + d.Write(b) + } + return d.Sum(nil) +} + +func ValidateSignatureValues(v byte, r, s *big.Int, homestead bool) bool { + if r.Cmp(common.Big1) < 0 || s.Cmp(common.Big1) < 0 { + return false + } + // reject upper range of s values (ECDSA malleability) + // see discussion in secp256k1/libsecp256k1/include/secp256k1.h + if homestead && s.Cmp(secp256k1_halfN) > 0 { + return false + } + // Frontier: allow s to be in full N range + return r.Cmp(secp256k1_N) < 0 && s.Cmp(secp256k1_N) < 0 && (v == 0 || v == 1) +} + +func Ecrecover(hash, sig []byte) ([]byte, error) { + return secp256k1.RecoverPubkey(hash, sig) +} diff --git a/vm/evm/evm_test.go b/vm/evm/evm_test.go new file mode 100644 index 00000000..9ee69f16 --- /dev/null +++ b/vm/evm/evm_test.go @@ -0,0 +1,60 @@ +package evm + +import ( + "testing" + "strings" + "fmt" + "DNA/common" + "DNA/vm/evm/abi" + "DNA/crypto" + "DNA/core/ledger" + "DNA/core/store/ChainStore" + "DNA/client" +) + +const ( + ABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_newgreeting","type":"string"}],"name":"setGreeting","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"type":"constructor"}]` + BIN = `6060604052341561000c57fe5b6040516104cf3803806104cf833981016040528080518201919050505b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190610080929190610088565b505b5061012d565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100c957805160ff19168380011785556100f7565b828001600101855582156100f7579182015b828111156100f65782518255916020019190600101906100db565b5b5090506101049190610108565b5090565b61012a91905b8082111561012657600081600090555060010161010e565b5090565b90565b6103938061013c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b514610051578063a413686214610063578063cfae3217146100bd575bfe5b341561005957fe5b610061610156565b005b341561006b57fe5b6100bb600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101ea565b005b34156100c557fe5b6100cd610205565b604051808060200182810382528381815181526020019150805190602001908083836000831461011c575b80518252602083111561011c576020820191506020810190506020830392506100f8565b505050905090810190601f1680156101485780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101e757600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b565b80600190805190602001906102009291906102ae565b505b50565b61020d61032e565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102a35780601f10610278576101008083540402835291602001916102a3565b820191906000526020600020905b81548152906001019060200180831161028657829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102ef57805160ff191683800117855561031d565b8280016001018555821561031d579182015b8281111561031c578251825591602001919060010190610301565b5b50905061032a9190610342565b5090565b602060405190810160405280600081525090565b61036491905b80821115610360576000816000905550600101610348565b5090565b905600a165627a7a72305820a89120798f8b367b08eefd82299ea98351bfcca35faaa1e4010fed675a54348e0029` +) +func TestEvm(t *testing.T) { + parsed, err := abi.JSON(strings.NewReader(ABI)) + if err != nil { + t.Fatal("parsed error:", err) + } + ledger.DefaultLedger = new(ledger.Ledger) + ledger.DefaultLedger.Store = ChainStore.NewLedgerStore() + ledger.DefaultLedger.Store.InitLedgerStore(ledger.DefaultLedger) + crypto.SetAlg(crypto.P256R1) + account, _ := client.NewAccount() + //input, err := parsed.Pack("", []common.Uint160{account.ProgramHash}) + input, err := parsed.Pack("", "testing") + if err != nil { + t.Fatal("input error:", err) + } + + evm := NewExecutionEngine() + code := common.FromHex(BIN) + + codes := append(code, input...) + codeHash, _ := common.ToCodeHash(codes) + + ret, err := evm.Create(account.ProgramHash, codes) + + fmt.Println("ret:", ret, "error:", err) + + input, err = parsed.Pack("greet") + fmt.Println("input:", input) + + ret, err = evm.Call(account.ProgramHash, codeHash, input) + fmt.Println("ret:", ret) + + ret0 := new(string) + + err = parsed.Unpack(ret0, "greet", ret) + fmt.Println("err:", err) + + fmt.Println("ret0:", *ret0) +} + + + diff --git a/vm/evm/execute_engine.go b/vm/evm/execute_engine.go new file mode 100644 index 00000000..77234fb5 --- /dev/null +++ b/vm/evm/execute_engine.go @@ -0,0 +1,105 @@ +package evm + +import ( + "DNA/common" + "sync/atomic" + "fmt" + "math/big" + "DNA/smartcontract/storage" +) + +type ExecutionEngine struct { + contract *Contract + opCode OpCode + stack *Stack + pc uint64 + memory *Memory + DBCache storage.DBCache + abort int32 + JumpTable [256]OpExec + time *big.Int + blockNumber *big.Int +} + +func NewExecutionEngine(dbCache storage.DBCache, time *big.Int, blockNumber *big.Int, gas common.Fixed64) *ExecutionEngine { + return &ExecutionEngine { + DBCache: dbCache, + time: time, + blockNumber: blockNumber, + JumpTable: NewOpExecList(), + } +} + +func (e *ExecutionEngine) Create(caller common.Uint160, code []byte) (ret []byte, err error) { + e.contract = NewContract(caller) + codeHash, err := common.ToCodeHash(code) + if err != nil { return nil, err } + e.contract.SetCode(code, codeHash) + ret, err = e.run() + if err != nil { return nil, err } + e.DBCache.SetCode(codeHash, ret) + return ret, nil +} + +func (e *ExecutionEngine) Call(caller common.Uint160, codeHash common.Uint160, input []byte) (ret []byte, err error) { + e.contract = NewContract(caller) + code, err := e.DBCache.GetCode(codeHash) + if err != nil { + return nil, err + } + e.contract.SetCallCode(code, input, codeHash) + ret, err = e.run() + if err != nil { return nil, err } + return ret, nil +} + +func (e *ExecutionEngine) CallCode(codeHash common.Uint160, input []byte) (ret []byte, err error) { + return ret, nil +} + +func (e *ExecutionEngine) DelegateCall(codeHash common.Uint160, toAddr common.Uint160, input []byte) (ret []byte, err error) { + return ret, nil +} + +func(e *ExecutionEngine) run() ([]byte, error) { + if v, ok := PrecompiledContracts[e.contract.CodeHash]; ok { + return v.Run(e.contract.Input) + } + + e.stack = newstack() + e.memory = NewMemory() + e.pc = uint64(0) + + for atomic.LoadInt32(&e.abort) == 0 { + op := e.contract.GetOp(e.pc) + operation := e.JumpTable[op] + fmt.Println("op:", operation.Name) + + if operation.Exec == nil { + return nil, fmt.Errorf("invalid opcode 0x%x", op) + } + + if err := operation.validateStack(e.stack); err != nil { + return nil, err + } + + res, err := operation.Exec(e) + + switch { + case err != nil: + return nil, err + case operation.halts: + return res, nil + case !operation.jumps: + e.pc++ + } + s := e.stack.len() + for i:=0; i 0 { + add := x.Add(x, y) + add.Mod(add, z) + push(e, common.U256(add)) + } else { + push(e, new(big.Int)) + } + return nil, nil +} + +func opMulMod(e *ExecutionEngine) ([]byte, error) { + x, y, z := pop(e), pop(e), pop(e) + if z.Cmp(bigZero) > 0 { + mul := x.Mul(x, y) + mul.Mod(mul, z) + push(e, common.U256(mul)) + } else { + push(e, new(big.Int)) + } + return nil, nil +} + +func opExp(e *ExecutionEngine) ([]byte, error) { + base, exponent := pop(e), pop(e) + push(e, common.Exp(base, exponent)) + return nil, nil +} + +func opSignExtend(e *ExecutionEngine) ([]byte, error) { + back := pop(e) + if back.Cmp(big.NewInt(31)) < 0 { + bit := uint(back.Uint64() * 8 + 7) + num := pop(e) + mask := back.Lsh(common.Big1, bit) + mask.Sub(mask, common.Big1) + if num.Bit(int(bit)) > 0 { + num.Or(num, mask.Not(mask)) + } else { + num.And(num, mask) + } + push(e, common.U256(num)) + } + return nil, nil +} + +func opLt(e *ExecutionEngine) ([]byte, error) { + x, y := pop(e), pop(e) + if x.Cmp(y) < 0 { + push(e, big.NewInt(1)) + } else { + push(e, big.NewInt(0)) + } + return nil, nil +} + +func opGt(e *ExecutionEngine) ([]byte, error) { + x, y := pop(e), pop(e) + if x.Cmp(y) > 0 { + push(e, big.NewInt(1)) + } else { + push(e, big.NewInt(0)) + } + return nil, nil +} + +func opSlt(e *ExecutionEngine) ([]byte, error) { + x, y := common.S256(pop(e)), common.S256(pop(e)) + if x.Cmp(common.S256(y)) < 0 { + push(e, big.NewInt(1)) + } else { + push(e, big.NewInt(0)) + } + return nil, nil +} + +func opSgt(e *ExecutionEngine) ([]byte, error) { + x, y := common.S256(pop(e)), common.S256(pop(e)) + if x.Cmp(y) > 0 { + push(e, big.NewInt(1)) + } else { + push(e, big.NewInt(0)) + } + return nil, nil +} + +func opEq(e *ExecutionEngine) ([]byte, error) { + x, y := pop(e), pop(e) + if x.Cmp(y) == 0 { + push(e, big.NewInt(1)) + } else { + push(e, big.NewInt(0)) + } + return nil, nil +} + +func opIsZero(e *ExecutionEngine) ([]byte, error) { + x := pop(e) + if x.Sign() > 0 { + push(e, big.NewInt(0)) + } else { + push(e, big.NewInt(1)) + } + return nil, nil +} + +func opAnd(e *ExecutionEngine) ([]byte, error) { + x, y := pop(e), pop(e) + push(e, x.And(x, y)) + return nil, nil +} + +func opXor(e *ExecutionEngine) ([]byte, error) { + x, y := pop(e), pop(e) + push(e, x.Xor(x, y)) + return nil, nil +} + +func opOr(e *ExecutionEngine) ([]byte, error) { + x, y := pop(e), pop(e) + push(e, x.Or(x, y)) + return nil, nil +} + +func opNot(e *ExecutionEngine) ([]byte, error) { + x := pop(e) + push(e, common.U256(x.Not(x))) + return nil, nil +} + +func opByte(e *ExecutionEngine) ([]byte, error) { + th, val := pop(e), pop(e) + if th.Cmp(big.NewInt(32)) < 0 { + b := big.NewInt(int64(common.PaddedBigBytes(val, 32)[th.Int64()])) + push(e, b) + } else { + push(e, big.NewInt(0)) + } + return nil, nil +} + +func opSha3(e *ExecutionEngine) ([]byte, error) { + offset, size := pop(e), pop(e) + data := e.memory.Get(offset.Int64(), size.Int64()) + hash := crypto.Keccak256(data) + push(e, new(big.Int).SetBytes(hash)) + return nil, nil +} + +func opAddress(e *ExecutionEngine) ([]byte, error) { + push(e, e.contract.CodeHash.Big()) + return nil, nil +} + +func opBalance(e *ExecutionEngine) ([]byte, error) { + programHash := BigToUint160(pop(e)) + balance := e.DBCache.GetBalance(programHash) + push(e, new(big.Int).Set(balance)) + return nil, nil +} + +func opOrigin(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opCaller(e *ExecutionEngine) ([]byte, error) { + push(e, e.contract.Caller.Big()) + return nil, nil +} + +func opCallValue(e *ExecutionEngine) ([]byte, error) { + push(e, e.contract.value) + return nil, nil +} + +func opCallDataLoad(e *ExecutionEngine) ([]byte, error) { + push(e, new(big.Int).SetBytes(common.GetData(e.contract.Input, pop(e), common.Big32))) + return nil, nil +} + +func opCallDataSize(e *ExecutionEngine) ([]byte, error) { + push(e, big.NewInt(int64(len(e.contract.Input)))) + return nil, nil +} + +func opCallDataCopy(e *ExecutionEngine) ([]byte, error) { + var ( + mOff = pop(e) + cOff = pop(e) + l = pop(e) + ) + e.memory.Set(mOff.Uint64(), l.Uint64(), common.GetData(e.contract.Input, cOff, l)) + return nil, nil +} + +func opCodeSize(e *ExecutionEngine) ([]byte, error) { + push(e, big.NewInt(int64(len(e.contract.Code)))) + return nil, nil +} + +func opCodeCopy(e *ExecutionEngine) ([]byte, error) { + var ( + mOff = pop(e) + cOff = pop(e) + l = pop(e) + ) + codeCopy := common.GetData(e.contract.Code, cOff, l) + e.memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) + return nil, nil +} + +func opGasPrice(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opExtCodeSize(e *ExecutionEngine) ([]byte, error) { + a := pop(e) + codeHash := BigToUint160(a) + push(e, a.SetInt64(int64(e.DBCache.GetCodeSize(codeHash)))) + return nil, nil +} + +func opExtCodeCopy(e *ExecutionEngine) ([]byte, error) { + var ( + codeHash = BigToUint160(pop(e)) + mOff = pop(e) + cOff = pop(e) + l = pop(e) + ) + state, err := e.DBCache.GetCode(codeHash) + if err != nil { + return nil, err + } + codeCopy := common.GetData(state, cOff, l) + e.memory.Set(mOff.Uint64(), l.Uint64(), codeCopy) + return nil, nil +} + +func opBlockHash(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opCoinBase(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opTimeStamp(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opNumber(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opDifficulty(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opGasLimit(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opPop(e *ExecutionEngine) ([]byte, error) { + pop(e) + return nil, nil +} + +func opMload(e *ExecutionEngine) ([]byte, error) { + offset := pop(e) + val := new(big.Int).SetBytes(e.memory.Get(offset.Int64(), 32)) + push(e, val) + return nil, nil +} + +func opMstore(e *ExecutionEngine) ([]byte, error) { + mStart, val := pop(e), pop(e) + e.memory.Set(mStart.Uint64(), 32, common.PaddedBigBytes(val, 32)) + return nil, nil +} + +func opMstore8(e *ExecutionEngine) ([]byte, error) { + off, val := pop(e).Int64(), pop(e).Int64() + e.memory.store[off] = byte(val & 0xff) + return nil, nil +} + +func opSload(e *ExecutionEngine) ([]byte, error) { + loc := BigToHash(pop(e)) + state, err := e.DBCache.GetState(e.contract.CodeHash, loc) + if err != nil { + return nil, err + } + push(e, state.Big()) + return nil, nil +} + +func opSstore(e *ExecutionEngine) ([]byte, error) { + loc := BigToHash(pop(e)) + val := pop(e) + e.DBCache.SetState(e.contract.CodeHash, loc, BigToHash(val)) + return nil, nil +} + +func opJump(e *ExecutionEngine) ([]byte, error) { + pos := pop(e) + if !e.contract.jumpdest.has(e.contract.CodeHash, e.contract.Code, pos) { + nop := e.contract.GetOp(pos.Uint64()) + return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) + } + e.pc = pos.Uint64() + return nil, nil +} + +func opJumpPi(e *ExecutionEngine) ([]byte, error) { + pos, cond := pop(e), pop(e) + if cond.Sign() == 0 { + e.pc++ + } else { + if !e.contract.jumpdest.has(e.contract.CodeHash, e.contract.Code, pos) { + nop := e.contract.GetOp(pos.Uint64()) + return nil, fmt.Errorf("invalid jump destination (%v) %v", nop, pos) + } + e.pc = pos.Uint64() + } + return nil, nil +} + +func opPc(e *ExecutionEngine) ([]byte, error) { + push(e, new(big.Int).SetUint64(e.pc)) + return nil, nil +} + +func opMsize(e *ExecutionEngine) ([]byte, error) { + push(e, big.NewInt(int64(e.memory.Len()))) + return nil, nil +} + +func opGas(e *ExecutionEngine) ([]byte, error) { + push(e, new(big.Int)) + return nil, nil +} + +func opJumpDest(e *ExecutionEngine) ([]byte, error) { + return nil, nil +} + +func opCreate(e *ExecutionEngine) ([]byte, error) { + pop(e) + var ( + offset, size = pop(e), pop(e) + input = e.memory.Get(offset.Int64(), size.Int64()) + ) + engine := NewExecutionEngine(e.DBCache, e.time, e.blockNumber, Fixed64(0)) + _, err := engine.Create(e.contract.Caller, input) + codeHash, _ := ToCodeHash(input) + if err != nil { + push(e, new(big.Int)) + } else { + push(e, codeHash.Big()) + } + return nil, nil +} + +func opCall(e *ExecutionEngine) ([]byte, error) { + pop(e) + hash, value := pop(e), pop(e) + value = common.U256(value) + inOffset, inSize := pop(e), pop(e) + retOffset, retSize := pop(e), pop(e) + pragramHash := BytesToUint160(hash.Bytes()) + args := e.memory.Get(inOffset.Int64(), inSize.Int64()) + + engine := NewExecutionEngine(e.DBCache, e.time, e.blockNumber, Fixed64(0)) + ret, err := engine.Call(pragramHash, e.contract.CodeHash, args) + if err != nil { + push(e, new(big.Int)) + } else { + push(e, big.NewInt(1)) + e.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret) + } + return nil, nil +} + +func opCallCode(e *ExecutionEngine) ([]byte, error) { + opCall(e) + return nil, nil +} + +func opDelegateCall(e *ExecutionEngine) ([]byte, error) { + opCall(e) + return nil, nil +} + +func opReturn(e *ExecutionEngine) ([]byte, error) { + offset, size := pop(e), pop(e) + ret := e.memory.GetPtr(offset.Int64(), size.Int64()) + return ret, nil +} + +func opSuicide(e *ExecutionEngine) ([]byte, error) { + programHash := BigToUint160(pop(e)) + balance := e.DBCache.GetBalance(programHash) + hash := BigToUint160(pop(e)) + e.DBCache.AddBalance(hash, balance) + e.DBCache.Suicide(e.contract.CodeHash) + return nil, nil +} + +func makePush(size uint64, pushByteSize int) executionFunc { + return func(e *ExecutionEngine) ([]byte, error) { + codeLen := len(e.contract.Code) + startMin := codeLen + if int(e.pc + 1) < startMin { + startMin = int(e.pc + 1) + } + + endMin := codeLen + if startMin + pushByteSize < endMin { + endMin = startMin + pushByteSize + } + + push(e, new(big.Int).SetBytes(common.RightPadBytes(e.contract.Code[startMin:endMin], pushByteSize))) + + e.pc += size + return nil, nil + } +} + +func makeDup(size int64) executionFunc { + return func(e *ExecutionEngine) ([]byte, error) { + e.stack.dup(int(size)) + return nil, nil + } +} + +func makeSwap(size int64) executionFunc { + return func(e *ExecutionEngine) ([]byte, error) { + e.stack.swap(int(size)) + return nil, nil + } +} + +func makeLog(size int) executionFunc { + return func(e *ExecutionEngine) ([]byte, error) { + return nil, nil + } +} + +func pop(e *ExecutionEngine) *big.Int { + return e.stack.pop() +} + +func push(e *ExecutionEngine, item *big.Int) { + e.stack.push(item) +} \ No newline at end of file diff --git a/vm/evm/instructions_test.go b/vm/evm/instructions_test.go new file mode 100644 index 00000000..40c6e011 --- /dev/null +++ b/vm/evm/instructions_test.go @@ -0,0 +1,46 @@ +package evm + +import ( + "testing" + "math/big" +) + +func TestOpAdd(t *testing.T) { + evm := NewExecutionEngine(nil, nil, nil) + evm.stack.push(big.NewInt(2)) + evm.stack.push(big.NewInt(3)) + opAdd(evm) + t.Log("add execute resutl:", evm.stack.pop()) +} + +func TestOpMul(t *testing.T) { + evm := NewExecutionEngine(nil, nil, nil) + evm.stack.push(big.NewInt(2)) + evm.stack.push(big.NewInt(3)) + opMul(evm) + t.Log("Mul execute resutl:", evm.stack.pop()) +} + +func TestOpSub(t *testing.T) { + evm := NewExecutionEngine(nil, nil, nil) + evm.stack.push(big.NewInt(2)) + evm.stack.push(big.NewInt(3)) + opSub(evm) + t.Log("Mul execute resutl:", evm.stack.pop()) +} + +func TestOpDiv(t *testing.T) { + evm := NewExecutionEngine(nil, nil, nil) + evm.stack.push(big.NewInt(-2)) + evm.stack.push(big.NewInt(3)) + opDiv(evm) + t.Log("Mul execute resutl:", evm.stack.pop()) +} + +func TestOpSdiv(t *testing.T) { + evm := NewExecutionEngine(nil, nil, nil) + evm.stack.push(big.NewInt(-2)) + evm.stack.push(big.NewInt(3)) + opSdiv(evm) + t.Log("Mul execute resutl:", evm.stack.pop()) +} \ No newline at end of file diff --git a/vm/evm/jump_table.go b/vm/evm/jump_table.go new file mode 100644 index 00000000..fed72b78 --- /dev/null +++ b/vm/evm/jump_table.go @@ -0,0 +1,671 @@ +package evm + + +type ( + executionFunc func(evm *ExecutionEngine) ([]byte, error) + stackValidationFunc func(*Stack) error +) + +type OpExec struct { + Name string + Exec executionFunc + validateStack stackValidationFunc + jumps bool + halts bool +} + +func NewOpExecList() [256]OpExec { + return [256]OpExec{ + STOP: { + Name:"STOP", + Exec: opStop, + validateStack: makeStackFunc(0, 0), + halts: true, + }, + ADD: { + Name: "ADD", + Exec: opAdd, + validateStack: makeStackFunc(2, 1), + }, + MUL: { + Name: "MUL", + Exec: opMul, + validateStack: makeStackFunc(2, 1), + }, + SUB: { + Name: "SUB", + Exec: opSub, + validateStack: makeStackFunc(2, 1), + }, + DIV: { + Name: "DIV", + Exec: opDiv, + validateStack: makeStackFunc(2, 1), + }, + SDIV:{ + Name: "SDIV", + Exec: opSdiv, + validateStack: makeStackFunc(2, 1), + }, + MOD: { + Name: "MOD", + Exec: opMod, + validateStack: makeStackFunc(2, 1), + }, + SMOD:{ + Name: "SMOD", + Exec: opSmod, + validateStack: makeStackFunc(2, 1), + }, + ADDMOD: { + Name: "ADDMOD", + Exec: opAddMod, + validateStack: makeStackFunc(3, 1), + }, + MULMOD: { + Name: "MULMOD", + Exec: opMulMod, + validateStack: makeStackFunc(3, 1), + }, + EXP: { + Name: "EXP", + Exec: opExp, + validateStack: makeStackFunc(2, 1), + }, + SIGNEXTEND: { + Name: "SIGNEXTEND", + Exec: opSignExtend, + validateStack: makeStackFunc(2, 1), + }, + LT: { + Name: "LT", + Exec: opLt, + validateStack: makeStackFunc(2, 1), + }, + GT: { + Name: "GT", + Exec: opGt, + validateStack: makeStackFunc(2, 1), + }, + SLT: { + Name: "SLT", + Exec: opSlt, + validateStack: makeStackFunc(2, 1), + }, + SGT: { + Name: "SGT", + Exec: opSgt, + validateStack: makeStackFunc(2, 1), + }, + EQ: { + Name: "EQ", + Exec: opEq, + validateStack: makeStackFunc(2, 1), + }, + ISZERO: { + Name: "ISZERO", + Exec: opIsZero, + validateStack: makeStackFunc(1, 1), + }, + AND: { + Name: "AND", + Exec: opAnd, + validateStack: makeStackFunc(2, 1), + }, + XOR: { + Name: "XOR", + Exec: opXor, + validateStack: makeStackFunc(2, 1), + }, + OR: { + Name: "OR", + Exec: opOr, + validateStack: makeStackFunc(2, 1), + }, + NOT: { + Name: "NOT", + Exec: opNot, + validateStack: makeStackFunc(1, 1), + }, + BYTE: { + Name: "BYTE", + Exec: opByte, + validateStack: makeStackFunc(2, 1), + }, + SHA3: { + Name: "SHA3", + Exec: opSha3, + validateStack: makeStackFunc(2, 1), + }, + ADDRESS: { + Name: "ADDRESS", + Exec: opAddress, + validateStack: makeStackFunc(0, 1), + }, + BALANCE: { + Name: "BALANCE", + Exec: opBalance, + validateStack: makeStackFunc(1, 1), + }, + ORIGIN: { + Name: "ORIGIN", + Exec: opOrigin, + validateStack: makeStackFunc(0, 1), + }, + CALLER: { + Name: "CALLER", + Exec: opCaller, + validateStack: makeStackFunc(0, 1), + }, + CALLVALUE: { + Name: "CALLVALUE", + Exec: opCallValue, + validateStack: makeStackFunc(0, 1), + }, + CALLDATALOAD: { + Name: "CALLDATALOAD", + Exec: opCallDataLoad, + validateStack: makeStackFunc(1, 1), + }, + CALLDATASIZE: { + Name: "CALLDATASIZE", + Exec: opCallDataSize, + validateStack: makeStackFunc(0, 1), + }, + CALLDATACOPY: { + Name: "CALLDATACOPY", + Exec: opCallDataCopy, + validateStack: makeStackFunc(3, 0), + }, + CODESIZE: { + Name: "CODESIZE", + Exec: opCodeSize, + validateStack: makeStackFunc(0, 1), + }, + CODECOPY: { + Name: "CODECOPY", + Exec: opCodeCopy, + validateStack: makeStackFunc(3, 0), + }, + GASPRICE: { + Name: "GASPRICE", + Exec: opGasPrice, + validateStack: makeStackFunc(0, 1), + }, + EXTCODESIZE: { + Name: "EXTCODESIZE", + Exec: opExtCodeSize, + validateStack: makeStackFunc(1, 1), + }, + EXTCODECOPY: { + Name: "EXTCODECOPY", + Exec: opExtCodeCopy, + validateStack: makeStackFunc(4, 0), + }, + BLOCKHASH: { + Name: "BLOCKHASH", + Exec: opBlockHash, + validateStack: makeStackFunc(1, 1), + }, + COINBASE: { + Name: "COINBASE", + Exec: opCoinBase, + validateStack: makeStackFunc(0, 1), + }, + TIMESTAMP: { + Name: "TIMESTAMP", + Exec: opTimeStamp, + validateStack: makeStackFunc(0, 1), + }, + NUMBER: { + Name: "NUMBER", + Exec: opNumber, + validateStack: makeStackFunc(0, 1), + }, + DIFFICULTY: { + Name: "DIFFICULTY", + Exec: opDifficulty, + validateStack: makeStackFunc(0, 1), + }, + GASLIMIT: { + Name: "GASLIMIT", + Exec: opGasLimit, + validateStack: makeStackFunc(0, 1), + }, + POP: { + Name: "POP", + Exec: opPop, + validateStack: makeStackFunc(1, 0), + }, + MLOAD: { + Name: "MLOAD", + Exec: opMload, + validateStack: makeStackFunc(1, 1), + }, + MSTORE: { + Name: "MSTORE", + Exec: opMstore, + validateStack: makeStackFunc(2, 0), + }, + MSTORE8: { + Name: "MSTORE8", + Exec: opMstore8, + validateStack: makeStackFunc(2, 0), + }, + SLOAD: { + Name: "SLOAD", + Exec: opSload, + validateStack: makeStackFunc(1, 1), + }, + SSTORE: { + Name: "SSTORE", + Exec: opSstore, + validateStack: makeStackFunc(2, 0), + }, + JUMP: { + Name: "JUMP", + Exec: opJump, + validateStack: makeStackFunc(1, 0), + jumps: true, + }, + JUMPI: { + Name: "JUMPI", + Exec: opJumpPi, + validateStack: makeStackFunc(2, 0), + jumps: true, + }, + PC: { + Name: "PC", + Exec: opPc, + validateStack: makeStackFunc(0, 1), + }, + MSIZE: { + Name: "MSIZE", + Exec: opMsize, + validateStack: makeStackFunc(0, 1), + }, + GAS: { + Name: "GAS", + Exec: opGas, + validateStack: makeStackFunc(0, 1), + }, + JUMPDEST: { + Name: "JUMPDEST", + Exec: opJumpDest, + validateStack: makeStackFunc(0, 0), + }, + PUSH1: { + Name: "PUSH1", + Exec: makePush(1, 1), + validateStack: makeStackFunc(0, 1), + }, + PUSH2: { + Name: "PUSH2", + Exec: makePush(2, 2), + validateStack: makeStackFunc(0, 1), + }, + PUSH3: { + Name: "PUSH3", + Exec: makePush(3, 3), + validateStack: makeStackFunc(0, 1), + }, + PUSH4: { + Name: "PUSH4", + Exec: makePush(4, 4), + validateStack: makeStackFunc(0, 1), + }, + PUSH5: { + Name: "PUSH5", + Exec: makePush(5, 5), + validateStack: makeStackFunc(0, 1), + }, + PUSH6: { + Name: "PUSH6", + Exec: makePush(6, 6), + validateStack: makeStackFunc(0, 1), + }, + PUSH7: { + Name: "PUSH7", + Exec: makePush(7, 7), + validateStack: makeStackFunc(0, 1), + }, + PUSH8: { + Name: "PUSH8", + Exec: makePush(8, 8), + validateStack: makeStackFunc(0, 1), + }, + PUSH9: { + Name: "PUSH9", + Exec: makePush(9, 9), + validateStack: makeStackFunc(0, 1), + }, + PUSH10: { + Name: "PUSH10", + Exec: makePush(10, 10), + validateStack: makeStackFunc(0, 1), + }, + PUSH11: { + Name: "PUSH11", + Exec: makePush(11, 11), + validateStack: makeStackFunc(0, 1), + }, + PUSH12: { + Name: "PUSH12", + Exec: makePush(12, 12), + validateStack: makeStackFunc(0, 1), + }, + PUSH13: { + Name: "PUSH13", + Exec: makePush(13, 13), + validateStack: makeStackFunc(0, 1), + }, + PUSH14: { + Name: "PUSH14", + Exec: makePush(14, 14), + validateStack: makeStackFunc(0, 1), + }, + PUSH15: { + Name: "PUSH15", + Exec: makePush(15, 15), + validateStack: makeStackFunc(0, 1), + }, + PUSH16: { + Name: "PUSH16", + Exec: makePush(16, 16), + validateStack: makeStackFunc(0, 1), + }, + PUSH17: { + Name: "PUSH17", + Exec: makePush(17, 17), + validateStack: makeStackFunc(0, 1), + }, + PUSH18: { + Name: "PUSH18", + Exec: makePush(18, 18), + validateStack: makeStackFunc(0, 1), + }, + PUSH19: { + Name: "PUSH19", + Exec: makePush(19, 19), + validateStack: makeStackFunc(0, 1), + }, + PUSH20: { + Name: "PUSH20", + Exec: makePush(20, 20), + validateStack: makeStackFunc(0, 1), + }, + PUSH21: { + Name: "PUSH21", + Exec: makePush(21, 21), + validateStack: makeStackFunc(0, 1), + }, + PUSH22: { + Name: "PUSH22", + Exec: makePush(22, 22), + validateStack: makeStackFunc(0, 1), + }, + PUSH23: { + Name: "PUSH23", + Exec: makePush(23, 23), + validateStack: makeStackFunc(0, 1), + }, + PUSH24: { + Name: "PUSH24", + Exec: makePush(24, 24), + validateStack: makeStackFunc(0, 1), + }, + PUSH25: { + Name: "PUSH25", + Exec: makePush(25, 25), + validateStack: makeStackFunc(0, 1), + }, + PUSH26: { + Name: "PUSH26", + Exec: makePush(26, 26), + validateStack: makeStackFunc(0, 1), + }, + PUSH27: { + Name: "PUSH27", + Exec: makePush(27, 27), + validateStack: makeStackFunc(0, 1), + }, + PUSH28: { + Name: "PUSH28", + Exec: makePush(28, 28), + validateStack: makeStackFunc(0, 1), + }, + PUSH29: { + Name: "PUSH29", + Exec: makePush(29, 29), + validateStack: makeStackFunc(0, 1), + }, + PUSH30: { + Name: "PUSH30", + Exec: makePush(30, 30), + validateStack: makeStackFunc(0, 1), + }, + PUSH31: { + Name: "PUSH31", + Exec: makePush(31, 31), + validateStack: makeStackFunc(0, 1), + }, + PUSH32: { + Name: "PUSH32", + Exec: makePush(32, 32), + validateStack: makeStackFunc(0, 1), + }, + DUP1: { + Name: "DUP1", + Exec: makeDup(1), + validateStack: makeDupStackFunc(1), + }, + DUP2: { + Name: "DUP2", + Exec: makeDup(2), + validateStack: makeDupStackFunc(2), + }, + DUP3: { + Name: "DUP3", + Exec: makeDup(3), + validateStack: makeDupStackFunc(3), + }, + DUP4: { + Name: "DUP4", + Exec: makeDup(4), + validateStack: makeDupStackFunc(4), + }, + DUP5: { + Name: "DUP5", + Exec: makeDup(5), + validateStack: makeDupStackFunc(5), + }, + DUP6: { + Name: "DUP6", + Exec: makeDup(6), + validateStack: makeDupStackFunc(6), + }, + DUP7: { + Name: "DUP7", + Exec: makeDup(7), + validateStack: makeDupStackFunc(7), + }, + DUP8: { + Name: "DUP8", + Exec: makeDup(8), + validateStack: makeDupStackFunc(8), + }, + DUP9: { + Name: "DUP9", + Exec: makeDup(9), + validateStack: makeDupStackFunc(9), + }, + DUP10: { + Name: "DUP10", + Exec: makeDup(10), + validateStack: makeDupStackFunc(10), + }, + DUP11: { + Name: "DUP11", + Exec: makeDup(11), + validateStack: makeDupStackFunc(11), + }, + DUP12: { + Name: "DUP12", + Exec: makeDup(12), + validateStack: makeDupStackFunc(12), + }, + DUP13: { + Name: "DUP13", + Exec: makeDup(13), + validateStack: makeDupStackFunc(13), + }, + DUP14: { + Name: "DUP14", + Exec: makeDup(14), + validateStack: makeDupStackFunc(14), + }, + DUP15: { + Name: "DUP15", + Exec: makeDup(15), + validateStack: makeDupStackFunc(15), + }, + DUP16: { + Name: "DUP16", + Exec: makeDup(16), + validateStack: makeDupStackFunc(16), + }, + SWAP1: { + Name: "SWAP1", + Exec: makeSwap(1), + validateStack: makeSwapStackFunc(2), + }, + SWAP2: { + Name: "SWAP2", + Exec: makeSwap(2), + validateStack: makeSwapStackFunc(3), + }, + SWAP3: { + Name: "SWAP3", + Exec: makeSwap(3), + validateStack: makeSwapStackFunc(4), + }, + SWAP4: { + Name: "SWAP4", + Exec: makeSwap(4), + validateStack: makeSwapStackFunc(5), + }, + SWAP5: { + Name: "SWAP5", + Exec: makeSwap(5), + validateStack: makeSwapStackFunc(6), + }, + SWAP6: { + Name: "SWAP6", + Exec: makeSwap(6), + validateStack: makeSwapStackFunc(7), + }, + SWAP7: { + Name: "SWAP7", + Exec: makeSwap(7), + validateStack: makeSwapStackFunc(8), + }, + SWAP8: { + Name: "SWAP8", + Exec: makeSwap(8), + validateStack: makeSwapStackFunc(9), + }, + SWAP9: { + Name: "SWAP9", + Exec: makeSwap(9), + validateStack: makeSwapStackFunc(10), + }, + SWAP10: { + Name: "SWAP10", + Exec: makeSwap(10), + validateStack: makeSwapStackFunc(11), + }, + SWAP11: { + Name: "SWAP11", + Exec: makeSwap(11), + validateStack: makeSwapStackFunc(12), + }, + SWAP12: { + Name: "SWAP12", + Exec: makeSwap(12), + validateStack: makeSwapStackFunc(13), + }, + SWAP13: { + Name: "SWAP13", + Exec: makeSwap(13), + validateStack: makeSwapStackFunc(14), + }, + SWAP14: { + Name: "SWAP14", + Exec: makeSwap(14), + validateStack: makeSwapStackFunc(15), + }, + SWAP15: { + Name: "SWAP15", + Exec: makeSwap(15), + validateStack: makeSwapStackFunc(15), + }, + SWAP16: { + Name: "SWAP16", + Exec: makeSwap(16), + validateStack: makeSwapStackFunc(16), + }, + LOG0: { + Name: "LOG0", + Exec: makeLog(0), + validateStack: makeStackFunc(2, 0), + }, + LOG1: { + Name: "LOG1", + Exec: makeLog(1), + validateStack: makeStackFunc(3, 0), + }, + LOG2: { + Name: "LOG2", + Exec: makeLog(2), + validateStack: makeStackFunc(4, 0), + }, + LOG3: { + Name: "LOG3", + Exec: makeLog(3), + validateStack: makeStackFunc(5, 0), + }, + LOG4: { + Name: "LOG4", + Exec: makeLog(4), + validateStack: makeStackFunc(6, 0), + }, + CREATE: { + Name: "CREATE", + Exec: opCreate, + validateStack: makeStackFunc(3, 1), + }, + CALL: { + Name: "CALL", + Exec: opCall, + validateStack: makeStackFunc(7, 1), + }, + CALLCODE: { + Name: "CALLCODE", + Exec: opCallCode, + validateStack: makeStackFunc(7, 1), + }, + RETURN: { + Name: "RETURN", + Exec: opReturn, + validateStack: makeStackFunc(2, 0), + halts: true, + }, + SELFDESTRUCT: { + Name: "SELFDESTRUCT", + Exec: opSuicide, + validateStack: makeStackFunc(1, 0), + halts: true, + }, + } +} + diff --git a/vm/evm/memory.go b/vm/evm/memory.go new file mode 100644 index 00000000..5f7bc948 --- /dev/null +++ b/vm/evm/memory.go @@ -0,0 +1,70 @@ +package evm + +import "fmt" + +type Memory struct { + store []byte + lastReturn []byte +} + +func NewMemory() *Memory{ + return &Memory{} +} + +func (m *Memory) Set(offset, size uint64, value []byte) error { + m.Resize(offset+size) + if size > 0 { + copy(m.store[offset:offset+size], value) + } + return nil +} + +func (m *Memory) Get(offset, size int64) (cpy []byte) { + if size == 0 { + return nil + } + if len(m.store) > int(offset) { + cpy = make([]byte, size) + copy(cpy, m.store[offset:offset+size]) + return + } + return +} + +func (m *Memory) GetPtr(offset, size int64) []byte { + if size == 0 { + return nil + } + if len(m.store) > int(offset) { + return m.store[offset:offset+size] + } + return nil +} + +func (m *Memory) Len() int { + return len(m.store) +} + +func (m *Memory) Data() []byte { + return m.store +} + +func (m *Memory) Resize(size uint64) { + if uint64(m.Len()) < size { + m.store = append(m.store, make([]byte, size-uint64(m.Len()))...) + } +} + +func (m *Memory) Print() { + fmt.Printf("### mem %d bytes ###\n", len(m.store)) + if len(m.store) > 0 { + addr := 0 + for i := 0; i+32 <= len(m.store); i += 32 { + fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) + addr++ + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("####################") +} \ No newline at end of file diff --git a/vm/evm/memory_table.go b/vm/evm/memory_table.go new file mode 100644 index 00000000..486d3cca --- /dev/null +++ b/vm/evm/memory_table.go @@ -0,0 +1,10 @@ +package evm + +import ( + "math/big" + "DNA/vm/evm/common" +) + +func memoryMStore(stack *Stack) *big.Int { + return common.CalcMemSize(stack.Back(0), big.NewInt(32)) +} \ No newline at end of file diff --git a/vm/evm/opcode.go b/vm/evm/opcode.go new file mode 100644 index 00000000..e7459dc0 --- /dev/null +++ b/vm/evm/opcode.go @@ -0,0 +1,174 @@ +package evm + +type OpCode byte + +const ( + // 0x0 range - arithmetic ops + STOP OpCode = iota + ADD + MUL + SUB + DIV + SDIV + MOD + SMOD + ADDMOD + MULMOD + EXP + SIGNEXTEND +) + +const ( + LT OpCode = iota + 0x10 + GT + SLT + SGT + EQ + ISZERO + AND + OR + XOR + NOT + BYTE + + SHA3 = 0x20 +) + +const ( + // 0x30 range - closure state + ADDRESS OpCode = 0x30 + iota + BALANCE + ORIGIN + CALLER + CALLVALUE + CALLDATALOAD + CALLDATASIZE + CALLDATACOPY + CODESIZE + CODECOPY + GASPRICE + EXTCODESIZE + EXTCODECOPY +) + +const ( + + // 0x40 range - block operations + BLOCKHASH OpCode = 0x40 + iota + COINBASE + TIMESTAMP + NUMBER + DIFFICULTY + GASLIMIT +) + +const ( + // 0x50 range - 'storage' and execution + POP OpCode = 0x50 + iota + MLOAD + MSTORE + MSTORE8 + SLOAD + SSTORE + JUMP + JUMPI + PC + MSIZE + GAS + JUMPDEST +) + + +const ( + LOG0 OpCode = 0xa0 + iota + LOG1 + LOG2 + LOG3 + LOG4 +) + +const ( + // 0x60 range + PUSH1 OpCode = 0x60 + iota + PUSH2 + PUSH3 + PUSH4 + PUSH5 + PUSH6 + PUSH7 + PUSH8 + PUSH9 + PUSH10 + PUSH11 + PUSH12 + PUSH13 + PUSH14 + PUSH15 + PUSH16 + PUSH17 + PUSH18 + PUSH19 + PUSH20 + PUSH21 + PUSH22 + PUSH23 + PUSH24 + PUSH25 + PUSH26 + PUSH27 + PUSH28 + PUSH29 + PUSH30 + PUSH31 + PUSH32 + DUP1 + DUP2 + DUP3 + DUP4 + DUP5 + DUP6 + DUP7 + DUP8 + DUP9 + DUP10 + DUP11 + DUP12 + DUP13 + DUP14 + DUP15 + DUP16 + SWAP1 + SWAP2 + SWAP3 + SWAP4 + SWAP5 + SWAP6 + SWAP7 + SWAP8 + SWAP9 + SWAP10 + SWAP11 + SWAP12 + SWAP13 + SWAP14 + SWAP15 + SWAP16 +) + + +const ( + PUSH OpCode = 0xb0 + iota + DUP + SWAP +) + +const ( + // 0xf0 range - closures + CREATE OpCode = 0xf0 + iota + CALL + CALLCODE + RETURN + DELEGATECALL + + SELFDESTRUCT = 0xff +) diff --git a/vm/evm/params.go b/vm/evm/params.go new file mode 100644 index 00000000..8233fb09 --- /dev/null +++ b/vm/evm/params.go @@ -0,0 +1,5 @@ +package evm + +const ( + StackLimit uint64 = 1024 // Maximum size of VM stack allowed. +) diff --git a/vm/evm/stack.go b/vm/evm/stack.go new file mode 100644 index 00000000..aa614446 --- /dev/null +++ b/vm/evm/stack.go @@ -0,0 +1,76 @@ +package evm + +import ( + "math/big" + "fmt" +) + +type Stack struct { + data []*big.Int +} + + +func newstack() *Stack { + return &Stack{data: make([]*big.Int, 0, 1024)} +} + +func (s *Stack) Data() []*big.Int { + return s.data +} + +func (s *Stack) push(b *big.Int) { + s.data = append(s.data, b) +} + +func (s *Stack) dup(n int) { + s.push(new(big.Int).Set(s.data[s.len() - n])) +} + +func (s *Stack) pushN(bs ...*big.Int) { + s.data = append(s.data, bs...) +} + +func (s *Stack) pop() (ret *big.Int) { + l := s.len() - 1 + ret = s.data[l] + s.data = s.data[:l] + return +} + +func (s *Stack) len() int { + return len(s.data) +} + +func (s *Stack) swap(n int) { + l := s.len() + l1 := l-n-1 + l2 := l-1 + s.data[l1], s.data[l2] = s.data[l2], s.data[l1] +} + +func (s *Stack) peek() *big.Int { + return s.data[s.len() - 1] +} + +func (s *Stack) Back(n int) *big.Int { + return s.data[s.len() - n - 1] +} + +func (s *Stack) require(n int) error { + if s.len() < n { + return fmt.Errorf("stack underflow (%d <=> %d)", len(s.data), n) + } + return nil +} + +func (s *Stack) Print() { + fmt.Println("### stack ###") + if len(s.data) > 0 { + for i, val := range s.Data() { + fmt.Printf("%-3d %v\n", i, val) + } + } else { + fmt.Println("-- empty --") + } + fmt.Println("#############") +} diff --git a/vm/evm/stack_table.go b/vm/evm/stack_table.go new file mode 100644 index 00000000..1b7eb342 --- /dev/null +++ b/vm/evm/stack_table.go @@ -0,0 +1,26 @@ +package evm + +import ( + "fmt" + "github.com/ethereum/go-ethereum/params" +) + +func makeStackFunc(pop, push int) stackValidationFunc { + return func(stack *Stack) error { + if err := stack.require(pop); err != nil { + return err + } + if stack.len() + push - pop > int(StackLimit) { + return fmt.Errorf("stack limit reached %d (%d)", stack.len(), params.StackLimit) + } + return nil + } +} + +func makeDupStackFunc(n int) stackValidationFunc { + return makeStackFunc(n, n+1) +} + +func makeSwapStackFunc(n int) stackValidationFunc { + return makeStackFunc(n, n) +} diff --git a/vm/evm/test_case/ballot/ballot.sol b/vm/evm/test_case/ballot/ballot.sol new file mode 100644 index 00000000..16cdf774 --- /dev/null +++ b/vm/evm/test_case/ballot/ballot.sol @@ -0,0 +1,144 @@ +pragma solidity ^0.4.0; + +/// @title Voting with delegation. +contract Ballot { + // This declares a new complex type which will + // be used for variables later. + // It will represent a single voter. + struct Voter { + uint weight; // weight is accumulated by delegation + bool voted; // if true, that person already voted + address delegate; // person delegated to + uint vote; // index of the voted proposal + } + + // This is a type for a single proposal. + struct Proposal { + bytes32 name; // short name (up to 32 bytes) + uint voteCount; // number of accumulated votes + } + + address public chairperson; + + // This declares a state variable that + // stores a `Voter` struct for each possible address. + mapping(address => Voter) public voters; + + // A dynamically-sized array of `Proposal` structs. + Proposal[] public proposals; + + /// Create a new ballot to choose one of `proposalNames`. + function Ballot(bytes32[] proposalNames) { + chairperson = msg.sender; + voters[chairperson].weight = 1; + + // For each of the provided proposal names, + // create a new proposal object and add it + // to the end of the array. + for (uint i = 0; i < proposalNames.length; i++) { + // `Proposal({...})` creates a temporary + // Proposal object and `proposals.push(...)` + // appends it to the end of `proposals`. + proposals.push(Proposal({ + name: proposalNames[i], + voteCount: 0 + })); + } + } + + // Give `voter` the right to vote on this ballot. + // May only be called by `chairperson`. + function giveRightToVote(address voter) { + // If the argument of `require` evaluates to `false`, + // it terminates and reverts all changes to + // the state and to Ether balances. It is often + // a good idea to use this if functions are + // called incorrectly. But watch out, this + // will currently also consume all provided gas + // (this is planned to change in the future). + require((msg.sender == chairperson) && !voters[voter].voted); + voters[voter].weight = 1; + } + + /// Delegate your vote to the voter `to`. + function delegate(address to) { + // assigns reference + Voter sender = voters[msg.sender]; + require(!sender.voted); + + // Self-delegation is not allowed. + require(to != msg.sender); + + // Forward the delegation as long as + // `to` also delegated. + // In general, such loops are very dangerous, + // because if they run too long, they might + // need more gas than is available in a block. + // In this case, the delegation will not be executed, + // but in other situations, such loops might + // cause a contract to get "stuck" completely. + while (voters[to].delegate != address(0)) { + to = voters[to].delegate; + + // We found a loop in the delegation, not allowed. + require(to != msg.sender); + } + + // Since `sender` is a reference, this + // modifies `voters[msg.sender].voted` + sender.voted = true; + sender.delegate = to; + Voter delegate = voters[to]; + if (delegate.voted) { + // If the delegate already voted, + // directly add to the number of votes + proposals[delegate.vote].voteCount += sender.weight; + } else { + // If the delegate did not vote yet, + // add to her weight. + delegate.weight += sender.weight; + } + } + + /// Give your vote (including votes delegated to you) + /// to proposal `proposals[proposal].name`. + function vote(uint proposal) { + Voter sender = voters[msg.sender]; + require(!sender.voted); + sender.voted = true; + sender.vote = proposal; + + // If `proposal` is out of the range of the array, + // this will throw automatically and revert all + // changes. + proposals[proposal].voteCount += sender.weight; + } + + /// @dev Computes the winning proposal taking all + /// previous votes into account. + function winningProposal() constant + returns (uint winningProposal) + { + uint winningVoteCount = 0; + for (uint p = 0; p < proposals.length; p++) { + if (proposals[p].voteCount > winningVoteCount) { + winningVoteCount = proposals[p].voteCount; + winningProposal = p; + } + } + } + + // Calls winningProposal() function to get the index + // of the winner contained in the proposals array and then + // returns the name of the winner + function winnerName() constant + returns (bytes32 winningName) + { + winningName = proposals[winningProposal()].name; + } + + function winnerCount() constant returns (uint count) + { + count = proposals[winningProposal()].voteCount; + } +} \ No newline at end of file diff --git a/vm/evm/test_case/ballot/ballot_test.go b/vm/evm/test_case/ballot/ballot_test.go new file mode 100644 index 00000000..ff475b5a --- /dev/null +++ b/vm/evm/test_case/ballot/ballot_test.go @@ -0,0 +1,93 @@ +package ballot + +import ( + "testing" + "DNA/vm/evm/test_case" + "fmt" + "math/big" + "DNA/client" +) + +const ( + ABI = `[{"constant":false,"inputs":[{"name":"proposal","type":"uint256"}],"name":"vote","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"name","type":"bytes32"},{"name":"voteCount","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"chairperson","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"}],"name":"delegate","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"winningProposal","outputs":[{"name":"winningProposal","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"voter","type":"address"}],"name":"giveRightToVote","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"voters","outputs":[{"name":"weight","type":"uint256"},{"name":"voted","type":"bool"},{"name":"delegate","type":"address"},{"name":"vote","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"winnerCount","outputs":[{"name":"count","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"winnerName","outputs":[{"name":"winningName","type":"bytes32"}],"payable":false,"type":"function"},{"inputs":[{"name":"proposalNames","type":"bytes32[]"}],"payable":false,"type":"constructor"}]` + BIN = `6060604052341561000c57fe5b604051610b31380380610b31833981016040528080518201919050505b600033600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160016000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060000181905550600090505b815181101561016b57600280548060010182816100f89190610173565b916000526020600020906002020160005b604060405190810160405280868681518110151561012357fe5b906020019060200201516000191681526020016000815250909190915060008201518160000190600019169055602082015181600101555050505b80806001019150506100db565b5b50506101d5565b8154818355818115116101a05760020281600202836000526020600020918201910161019f91906101a5565b5b505050565b6101d291905b808211156101ce57600060008201600090556001820160009055506002016101ab565b5090565b90565b61094d806101e46000396000f30060606040523615610097576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630121b93f14610099578063013cf08b146100b95780632e4176cf146100fc5780635c19a95c1461014e578063609ff1bd146101845780639e7b8d61146101aa578063a3ec138d146101e0578063caa02e081461026f578063e2ba53f014610295575bfe5b34156100a157fe5b6100b760048080359060200190919050506102c3565b005b34156100c157fe5b6100d76004808035906020019091905050610386565b6040518083600019166000191681526020018281526020019250505060405180910390f35b341561010457fe5b61010c6103ba565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561015657fe5b610182600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506103e0565b005b341561018c57fe5b6101946106d3565b6040518082815260200191505060405180910390f35b34156101b257fe5b6101de600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061075a565b005b34156101e857fe5b610214600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190505061085c565b60405180858152602001841515151581526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200182815260200194505050505060405180910390f35b341561027757fe5b61027f6108b9565b6040518082815260200191505060405180910390f35b341561029d57fe5b6102a56108ed565b60405180826000191660001916815260200191505060405180910390f35b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff161515156103255760006000fd5b60018160010160006101000a81548160ff021916908315150217905550818160020181905550806000015460028381548110151561035f57fe5b906000526020600020906002020160005b50600101600082825401925050819055505b5050565b60028181548110151561039557fe5b906000526020600020906002020160005b915090508060000154908060010154905082565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002091508160010160009054906101000a900460ff161515156104445760006000fd5b3373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141515156104805760006000fd5b5b600073ffffffffffffffffffffffffffffffffffffffff16600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156105bf57600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff1692503373ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141515156105ba5760006000fd5b610481565b60018260010160006101000a81548160ff021916908315150217905550828260010160016101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002090508060010160009054906101000a900460ff16156106b65781600001546002826002015481548110151561068f57fe5b906000526020600020906002020160005b50600101600082825401925050819055506106cd565b816000015481600001600082825401925050819055505b5b505050565b60006000600060009150600090505b60028054905081101561075457816002828154811015156106ff57fe5b906000526020600020906002020160005b506001015411156107465760028181548110151561072a57fe5b906000526020600020906002020160005b506001015491508092505b5b80806001019150506106e2565b5b505090565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480156108045750600160008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060010160009054906101000a900460ff16155b15156108105760006000fd5b6001600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600001819055505b50565b60016020528060005260406000206000915090508060000154908060010160009054906101000a900460ff16908060010160019054906101000a900473ffffffffffffffffffffffffffffffffffffffff16908060020154905084565b600060026108c56106d3565b8154811015156108d157fe5b906000526020600020906002020160005b506001015490505b90565b600060026108f96106d3565b81548110151561090557fe5b906000526020600020906002020160005b506000015490505b905600a165627a7a723058208879ee0c993f9f05c97a82355a2500a2c5cf885110540078b7ef5aaaa6dc51260029` +) + +func TestGetSigner(t *testing.T) { + t.Log("testing greet start") + + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN, [][32]byte{[32]byte{1}, [32]byte{2}, [32]byte{3}}) + if err != nil { t.Errorf("new engine error:%v", err)} + account1, _ := client.NewAccount() + account2, _ := client.NewAccount() + account3, _ := client.NewAccount() + account4, _ := client.NewAccount() + + fmt.Println("deploy finished.") + input, err := parsed.Pack("giveRightToVote", account1.ProgramHash) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + + input, err = parsed.Pack("giveRightToVote", account2.ProgramHash) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + + input, err = parsed.Pack("giveRightToVote", account3.ProgramHash) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + + input, err = parsed.Pack("giveRightToVote", account4.ProgramHash) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + + input, err = parsed.Pack("vote", big.NewInt(1)) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + // + input, err = parsed.Pack("vote", big.NewInt(2)) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(account1.ProgramHash, *codeHash, input) + //// + input, err = parsed.Pack("vote", big.NewInt(2)) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(account2.ProgramHash, *codeHash, input) + // + //input, err = parsed.Pack("vote", big.NewInt(2)) + //if err != nil { t.Errorf("pack error:%v", err)} + //ret, err = evm.Call(*account, *codeHash, input) + // + //input, err = parsed.Pack("vote", big.NewInt(2)) + //if err != nil { t.Errorf("pack error:%v", err)} + //ret, err = evm.Call(*account, *codeHash, input) + // + //input, err = parsed.Pack("vote", big.NewInt(1)) + //if err != nil { t.Errorf("pack error:%v", err)} + //ret, err = evm.Call(*account, *codeHash, input) + // + //input, err = parsed.Pack("vote", big.NewInt(1)) + //if err != nil { t.Errorf("pack error:%v", err)} + //ret, err = evm.Call(*account, *codeHash, input) + + input, err = parsed.Pack("winningProposal") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("winningProposal ret:", ret) + + input, err = parsed.Pack("winnerName") + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("winnerName ret:", ret) + //var ret0 [32]byte + //err = parsed.Unpack(&ret0, "winnerName", ret) + //if err != nil { t.Errorf("unpack error:%v", err)} + //fmt.Println("ret0:", ret0) + + + input, err = parsed.Pack("winnerCount") + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("winnerCount ret:", ret) + //err = parsed.Unpack(&1, "winnerName", ret) + //if err != nil { t.Errorf("unpack error:%v", err)} + //t.Log("ret0:", ret0) + t.Log("testing greet end") +} + diff --git a/vm/evm/test_case/coin/coin.sol b/vm/evm/test_case/coin/coin.sol new file mode 100644 index 00000000..224613ca --- /dev/null +++ b/vm/evm/test_case/coin/coin.sol @@ -0,0 +1,25 @@ +pragma solidity ^0.4.11; +contract Coin { + +//关键字“public”使变量能从合约外部访问。 + address public minter; + mapping (address => uint) public balances; + +//事件让轻客户端能高效的对变化做出反应。 + event Sent(address from, address to, uint amount); + +//这个构造函数的代码仅仅只在合约创建的时候被运行。 + function Coin() { + minter = msg.sender; + } + function mint(address receiver, uint amount) { + if (msg.sender != minter) return; + balances[receiver] += amount; + } + function send(address receiver, uint amount) { + if (balances[msg.sender] < amount) return; + balances[msg.sender] -= amount; + balances[receiver] += amount; + Sent(msg.sender, receiver, amount); + } +} \ No newline at end of file diff --git a/vm/evm/test_case/coin/coin_test.go b/vm/evm/test_case/coin/coin_test.go new file mode 100644 index 00000000..0864e347 --- /dev/null +++ b/vm/evm/test_case/coin/coin_test.go @@ -0,0 +1,70 @@ +package coin + +import ( + "testing" + "DNA/vm/evm/test_case" + "DNA/client" + "fmt" + "math/big" +) + +const ( + ABI = `[{"constant":true,"inputs":[],"name":"minter","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balances","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"name":"mint","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"}],"name":"send","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"getBalance","outputs":[{"name":"amount","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Sent","type":"event"}]` + BIN = `6060604052341561000c57fe5b5b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b6104b78061005f6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063075461721461006757806327e235e3146100b957806340c10f1914610103578063d0679d3414610142578063f8b2cb4f14610181575bfe5b341561006f57fe5b6100776101cb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34156100c157fe5b6100ed600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101f1565b6040518082815260200191505060405180910390f35b341561010b57fe5b610140600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610209565b005b341561014a57fe5b61017f600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506102b7565b005b341561018957fe5b6101b5600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610441565b6040518082815260200191505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60016020528060005260406000206000915090505481565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610265576102b3565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b5050565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156103035761043d565b80600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055507f3990db2d31862302a685e8086b5755072a6e2b5b780af1ee81ece35ee3cd3345338383604051808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001828152602001935050505060405180910390a15b5050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b9190505600a165627a7a7230582067e94cc6b43734c17b9205e4f12f59a693bc7e3f29269dcab79013f764268df00029` +) + +func TestCoin(t *testing.T) { + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN) + if err != nil { t.Errorf("new engine error:%v", err)} + + account1, _ := client.NewAccount() + account2, _ := client.NewAccount() + account3, _ := client.NewAccount() + //account4, _ := client.NewAccount() + + input, err := parsed.Pack("mint", account1.ProgramHash, big.NewInt(100)) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + fmt.Println("ret0", ret) + + input, err = parsed.Pack("mint", account2.ProgramHash, big.NewInt(100)) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("ret1", ret) + + input, err = parsed.Pack("mint", account, big.NewInt(100)) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("ret1", ret) + + input, err = parsed.Pack("getBalance", account1.ProgramHash) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("ret2", ret) + var i *big.Int + err = parsed.Unpack(&i, "getBalance", ret) + if err != nil { t.Errorf("unpack error:%v", err)} + fmt.Println("ret2", i) + + input, err = parsed.Pack("send", account3.ProgramHash, big.NewInt(10)) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("ret3", ret) + + input, err = parsed.Pack("getBalance", account3.ProgramHash) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("ret4", ret) + err = parsed.Unpack(&i, "getBalance", ret) + if err != nil { t.Errorf("unpack error:%v", err)} + fmt.Println("re4", i) + + input, err = parsed.Pack("getBalance", account) + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + fmt.Println("ret4", ret) + err = parsed.Unpack(&i, "getBalance", ret) + if err != nil { t.Errorf("unpack error:%v", err)} + fmt.Println("re4", i) + +} diff --git a/vm/evm/test_case/deal/deal.sol b/vm/evm/test_case/deal/deal.sol new file mode 100644 index 00000000..aa0f6aaf --- /dev/null +++ b/vm/evm/test_case/deal/deal.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.4.0; +contract Deal { + address private signer; + address public receiver; + + function Deal() { + signer = msg.sender; + receiver = 0x4e2d29c060eefc41a4f5330ecb67da94e0eed266; + } + + + function releasePayment() { + receiver.transfer(this.balance); + } + + function getReceiver() returns (address retVal) { + return receiver; + } + + function getSigner() returns (address retVal) { + return signer; + } + + function getBalance() returns (uint balance) { + return receiver.balance; + } +} \ No newline at end of file diff --git a/vm/evm/test_case/deal/deal_test.go b/vm/evm/test_case/deal/deal_test.go new file mode 100644 index 00000000..2d86ecbb --- /dev/null +++ b/vm/evm/test_case/deal/deal_test.go @@ -0,0 +1,87 @@ +package deal + +import ( + "testing" + "DNA/vm/evm/test_case" + "DNA/common" + "fmt" +) + +const ( + ABI = `[{"constant":false,"inputs":[],"name":"getSignerBalance","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"getReceiverBalance","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"getSigner","outputs":[{"name":"retVal","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"getReceiver","outputs":[{"name":"retVal","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"releasePayment","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"receiver","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"inputs":[],"payable":false,"type":"constructor"}]` + BIN = `6060604052341561000c57fe5b5b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550734e2d29c060eefc41a4f5330ecb67da94e0eed266600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b6103ca806100b46000396000f30060606040523615610076576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630d405c581461007857806310fb7bc61461009e5780637ac3c02f146100c457806398aca92214610116578063d116c8c414610168578063f7260d3e1461017a575bfe5b341561008057fe5b6100886101cc565b6040518082815260200191505060405180910390f35b34156100a657fe5b6100ae61020e565b6040518082815260200191505060405180910390f35b34156100cc57fe5b6100d4610250565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561011e57fe5b61012661027b565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b341561017057fe5b6101786102a6565b005b341561018257fe5b61018a610378565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163190505b90565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163190505b90565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b90565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b90565b3373ffffffffffffffffffffffffffffffffffffffff16600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156103035760006000fd5b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051809050600060405180830381858888f19350505050505b565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a723058206c5dadb232323c6fde39fb4eb69a4df3e260f9a357f5a01434e2c61977b80dcb0029` +) + +func TestGetSigner(t *testing.T) { + t.Log("testing greet start") + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN) + if err != nil { t.Errorf("new engine error:%v", err)} + input, err := parsed.Pack("getSigner") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + ret0 := new(common.Uint160) + err = parsed.Unpack(ret0, "getSigner", ret) + if err != nil { t.Errorf("unpack error:%v", err)} + t.Log("ret0:", *ret0) + t.Log("testing greet end") +} + +func TestGetReceiver(t *testing.T) { + t.Log("testing greet start") + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN) + if err != nil { t.Errorf("new engine error:%v", err)} + input, err := parsed.Pack("getReceiver") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + ret1 := new(common.Uint160) + err = parsed.Unpack(ret1, "getReceiver", ret) + if err != nil { t.Errorf("unpack error:%v", err)} + t.Log(fmt.Sprintf("ret1:%x", *ret1)) + t.Log("testing greet end") +} + +func TestReleasePayment(t *testing.T) { + t.Log("testing greet start") + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN) + if err != nil { t.Errorf("new engine error:%v", err)} + input, err := parsed.Pack("releasePayment") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + t.Log("ret3:", ret) + //ret2 := new(common.Uint160) + //err = parsed.Unpack(ret2, "releasePayment", ret) + //if err != nil { t.Errorf("unpack error:%v", err)} + //t.Log(fmt.Sprintf("ret1:%x", *ret2)) + t.Log("testing greet end") +} + + +func TestReceiverBalance(t *testing.T) { + t.Log("testing receiver balance") + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN) + if err != nil { t.Errorf("new engine error:%v", err)} + input, err := parsed.Pack("getReceiverBalance") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + t.Log("ret4:", ret) + //ret2 := new(common.Uint160) + //err = parsed.Unpack(ret2, "releasePayment", ret) + //if err != nil { t.Errorf("unpack error:%v", err)} + //t.Log(fmt.Sprintf("ret1:%x", *ret2)) + t.Log("testing greet end") +} + +func TestSignerBalance(t *testing.T) { + t.Log("testing receiver balance") + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN) + if err != nil { t.Errorf("new engine error:%v", err)} + input, err := parsed.Pack("getSignerBalance") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + t.Log("ret5:", ret) + //ret2 := new(common.Uint160) + //err = parsed.Unpack(ret2, "releasePayment", ret) + //if err != nil { t.Errorf("unpack error:%v", err)} + //t.Log(fmt.Sprintf("ret1:%x", *ret2)) + t.Log("testing greet end") +} diff --git a/vm/evm/test_case/evm.go b/vm/evm/test_case/evm.go new file mode 100644 index 00000000..ad58729e --- /dev/null +++ b/vm/evm/test_case/evm.go @@ -0,0 +1,37 @@ +package test_case + +import ( + "DNA/core/ledger" + "DNA/crypto" + "DNA/core/store/ChainStore" + client "DNA/account" + "DNA/vm/evm" + "strings" + "DNA/vm/evm/abi" + "DNA/common" + "time" + "math/big" +) + +func NewEngine(ABI, BIN string, params ...interface{}) (*common.Uint160, *common.Uint160, *evm.ExecutionEngine, *abi.ABI, error) { + ledger.DefaultLedger = new(ledger.Ledger) + ledger.DefaultLedger.Store = ChainStore.NewLedgerStore() + ledger.DefaultLedger.Store.InitLedgerStore(ledger.DefaultLedger) + crypto.SetAlg(crypto.P256R1) + account, _ := client.NewAccount() + t := time.Now().Unix() + e := evm.NewExecutionEngine(nil, big.NewInt(t), big.NewInt(1), common.Fixed64(0)) + parsed, err := abi.JSON(strings.NewReader(ABI)) + if err != nil { return nil, nil, nil, nil, err } + input, err := parsed.Pack("", params...) + if err != nil { return nil, nil, nil, nil, err } + code := common.FromHex(BIN) + codes := append(code, input...) + codeHash, _ := common.ToCodeHash(codes) + _, err = e.Create(account.ProgramHash, codes) + if err != nil { return nil, nil, nil, nil, err } + return &codeHash, &account.ProgramHash, e, &parsed, nil +} + + + diff --git a/vm/evm/test_case/example/example.sol b/vm/evm/test_case/example/example.sol new file mode 100644 index 00000000..62d6792b --- /dev/null +++ b/vm/evm/test_case/example/example.sol @@ -0,0 +1,32 @@ +pragma solidity ^0.4.0; +contract HelloWorld +{ + address creator; + string greeting; + + function HelloWorld(string _greeting) public + { + creator = msg.sender; + greeting = _greeting; + } + + function greet() constant returns (string) + { + return greeting; + } + + function setGreeting(string _newgreeting) + { + greeting = _newgreeting; + } + + /********** + Standard kill() function to recover funds + **********/ + + function kill() + { + if (msg.sender == creator) + suicide(creator); // kills this contract and sends remaining funds back to creator + } +} \ No newline at end of file diff --git a/vm/evm/test_case/example/example_test.go b/vm/evm/test_case/example/example_test.go new file mode 100644 index 00000000..24129ee9 --- /dev/null +++ b/vm/evm/test_case/example/example_test.go @@ -0,0 +1,48 @@ +package test + +import ( + "testing" + "DNA/vm/evm/test_case" +) + +const ( + ABI = `[{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_newgreeting","type":"string"}],"name":"setGreeting","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"inputs":[{"name":"_greeting","type":"string"}],"payable":false,"type":"constructor"}]` + BIN = `6060604052341561000c57fe5b6040516104cf3803806104cf833981016040528080518201919050505b33600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019080519060200190610080929190610088565b505b5061012d565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100c957805160ff19168380011785556100f7565b828001600101855582156100f7579182015b828111156100f65782518255916020019190600101906100db565b5b5090506101049190610108565b5090565b61012a91905b8082111561012657600081600090555060010161010e565b5090565b90565b6103938061013c6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b514610051578063a413686214610063578063cfae3217146100bd575bfe5b341561005957fe5b610061610156565b005b341561006b57fe5b6100bb600480803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919050506101ea565b005b34156100c557fe5b6100cd610205565b604051808060200182810382528381815181526020019150805190602001908083836000831461011c575b80518252602083111561011c576020820191506020810190506020830392506100f8565b505050905090810190601f1680156101485780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101e757600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5b565b80600190805190602001906102009291906102ae565b505b50565b61020d61032e565b60018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156102a35780601f10610278576101008083540402835291602001916102a3565b820191906000526020600020905b81548152906001019060200180831161028657829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106102ef57805160ff191683800117855561031d565b8280016001018555821561031d579182015b8281111561031c578251825591602001919060010190610301565b5b50905061032a9190610342565b5090565b602060405190810160405280600081525090565b61036491905b80821115610360576000816000905550600101610348565b5090565b905600a165627a7a72305820a89120798f8b367b08eefd82299ea98351bfcca35faaa1e4010fed675a54348e0029` +) + +func TestGreet(t *testing.T) { + t.Log("testing greet start") + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN, "testing") + if err != nil { t.Errorf("new engine error:%v", err)} + input, err := parsed.Pack("greet") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + ret0 := new(string) + err = parsed.Unpack(ret0, "greet", ret) + if err != nil { t.Errorf("unpack error:%v", err)} + t.Log("ret0:", *ret0) + t.Log("testing greet end") + +} + +func TestSetGreet(t *testing.T) { + t.Log("testing set greet start") + + codeHash, account, evm, parsed, err := test_case.NewEngine(ABI, BIN, "testing") + if err != nil { t.Errorf("new engine error:%v", err)} + + input, err := parsed.Pack("setGreeting", "hello world!") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err := evm.Call(*account, *codeHash, input) + + input, err = parsed.Pack("greet") + if err != nil { t.Errorf("pack error:%v", err)} + ret, err = evm.Call(*account, *codeHash, input) + ret0 := new(string) + err = parsed.Unpack(ret0, "greet", ret) + if err != nil { t.Errorf("unpack error:%v", err)} + t.Log("ret0:", *ret0) + + t.Log("testing set greet end") + +} diff --git a/vm/execution_context.go b/vm/execution_context.go deleted file mode 100755 index 96b5150e..00000000 --- a/vm/execution_context.go +++ /dev/null @@ -1,29 +0,0 @@ -package vm - -import "DNA/vm/utils" - -type ExecutionContext struct { - Script []byte - OpReader *utils.VmReader - PushOnly bool - BreakPoints []uint - InstructionPointer int -} - -func NewExecutionContext(script []byte, pushOnly bool, breakPoints []uint) *ExecutionContext { - var executionContext ExecutionContext - executionContext.Script = script - executionContext.OpReader = utils.NewVmReader(script) - executionContext.PushOnly = pushOnly - executionContext.BreakPoints = breakPoints - executionContext.InstructionPointer = executionContext.OpReader.Position() - return &executionContext -} - -func (ec *ExecutionContext) NextInstruction() OpCode { - return OpCode(ec.Script[ec.OpReader.Position()]) -} - -func (ec *ExecutionContext) Clone() *ExecutionContext { - return NewExecutionContext(ec.Script, ec.PushOnly, ec.BreakPoints) -} diff --git a/vm/execution_engine.go b/vm/execution_engine.go deleted file mode 100755 index b37db36b..00000000 --- a/vm/execution_engine.go +++ /dev/null @@ -1,201 +0,0 @@ -package vm - -import ( - "DNA/vm/interfaces" - "DNA/vm/utils" - "io" - _ "math/big" - _ "sort" -) - -const MAXSTEPS int = 1200 - -func NewExecutionEngine(container interfaces.IScriptContainer, crypto interfaces.ICrypto, maxSteps int, table interfaces.IScriptTable, service *InteropService) *ExecutionEngine { - var engine ExecutionEngine - - engine.crypto = crypto - engine.table = table - - engine.scriptContainer = container - engine.invocationStack = utils.NewRandAccessStack() - engine.opCount = 0 - - engine.evaluationStack = utils.NewRandAccessStack() - engine.altStack = utils.NewRandAccessStack() - engine.state = BREAK - - engine.context = nil - engine.opCode = 0 - - engine.maxSteps = maxSteps - - if service != nil { - engine.service = service - } - - engine.service = NewInteropService() - - return &engine -} - -type ExecutionEngine struct { - crypto interfaces.ICrypto - table interfaces.IScriptTable - service *InteropService - - scriptContainer interfaces.IScriptContainer - invocationStack *utils.RandomAccessStack - opCount int - - maxSteps int - - evaluationStack *utils.RandomAccessStack - altStack *utils.RandomAccessStack - state VMState - - context *ExecutionContext - - //current opcode - opCode OpCode -} - -func (e *ExecutionEngine) GetState() VMState { - return e.state -} - -func (e *ExecutionEngine) GetEvaluationStack() *utils.RandomAccessStack { - return e.evaluationStack -} - -func (e *ExecutionEngine) GetExecuteResult() bool { - return AssertStackItem(e.evaluationStack.Pop()).GetBoolean() -} - -func (e *ExecutionEngine) ExecutingScript() []byte { - context := AssertExecutionContext(e.invocationStack.Peek(0)) - if context != nil { - return context.Script - } - return nil -} - -func (e *ExecutionEngine) CallingScript() []byte { - if e.invocationStack.Count() > 1 { - context := AssertExecutionContext(e.invocationStack.Peek(1)) - if context != nil { - return context.Script - } - return nil - } - return nil -} - -func (e *ExecutionEngine) EntryScript() []byte { - context := AssertExecutionContext(e.invocationStack.Peek(e.invocationStack.Count() - 1)) - if context != nil { - return context.Script - } - return nil -} - -func (e *ExecutionEngine) LoadScript(script []byte, pushOnly bool) { - e.invocationStack.Push(NewExecutionContext(script, pushOnly, nil)) -} - -func (e *ExecutionEngine) Execute() { - e.state = e.state & (^BREAK) - for { - if e.state == FAULT || e.state == HALT || e.state == BREAK { - break - } - e.StepInto() - } -} - -func (e *ExecutionEngine) StepInto() { - if e.invocationStack.Count() == 0 { - e.state = VMState(e.state | HALT) - } - if e.state&HALT == HALT || e.state&FAULT == FAULT { - return - } - context := AssertExecutionContext(e.invocationStack.Pop()) - if context.InstructionPointer >= len(context.Script) { - e.opCode = RET - } - for { - opCode, err := context.OpReader.ReadByte() - if err == io.EOF && opCode == 0 { - return - } - e.opCount++ - state, err := e.ExecuteOp(OpCode(opCode), context) - if state == VMState(HALT) { - e.state = VMState(e.state | HALT) - return - } - } -} - -func (e *ExecutionEngine) ExecuteOp(opCode OpCode, context *ExecutionContext) (VMState, error) { - if opCode > PUSH16 && opCode != RET && context.PushOnly { - return FAULT, nil - } - if opCode > PUSH16 && e.opCount > e.maxSteps { - return FAULT, nil - } - if opCode >= PUSHBYTES1 && opCode <= PUSHBYTES75 { - err := pushData(e, context.OpReader.ReadBytes(int(opCode))) - if err != nil { - return FAULT, err - } - return NONE, nil - } - e.opCode = opCode - e.context = context - opExec := OpExecList[opCode] - if opExec.Exec == nil { - return FAULT, nil - } - state, err := opExec.Exec(e) - if err != nil { - return state, err - } - return NONE, nil -} - -func (e *ExecutionEngine) StepOut() { - e.state = e.state & (^BREAK) - c := e.invocationStack.Count() - for { - if e.state == FAULT || e.state == HALT || e.state == BREAK || e.invocationStack.Count() >= c { - break - } - e.StepInto() - } -} - -func (e *ExecutionEngine) StepOver() { - if e.state == FAULT || e.state == HALT { - return - } - e.state = e.state & (^BREAK) - c := e.invocationStack.Count() - for { - if e.state == FAULT || e.state == HALT || e.state == BREAK || e.invocationStack.Count() > c { - break - } - e.StepInto() - } -} - -func (e *ExecutionEngine) AddBreakPoint(position uint) { - //b := e.context.BreakPoints - //b = append(b, position) -} - -func (e *ExecutionEngine) RemoveBreakPoint(position uint) bool { - //if e.invocationStack.Count() == 0 { return false } - //b := e.context.BreakPoints - return true -} diff --git a/vm/func_arithmetic.go b/vm/func_arithmetic.go deleted file mode 100755 index 4c86400f..00000000 --- a/vm/func_arithmetic.go +++ /dev/null @@ -1,90 +0,0 @@ -package vm - -func opBigInt(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - x := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - err := pushData(e, BigIntOp(x, e.opCode)) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opNot(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - x := AssertStackItem(e.evaluationStack.Pop()).GetBoolean() - err := pushData(e, !x) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opNz(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - x := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - err := pushData(e, BigIntComp(x, e.opCode)) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opBigIntZip(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - x1 := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - err := pushData(e, BigIntZip(x1, x2, e.opCode)) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opBoolZip(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := AssertStackItem(e.evaluationStack.Pop()).GetBoolean() - x1 := AssertStackItem(e.evaluationStack.Pop()).GetBoolean() - err := pushData(e, BoolZip(x1, x2, e.opCode)) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opBigIntComp(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - x1 := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - err := pushData(e, BigIntMultiComp(x1, x2, e.opCode)) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opWithIn(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 3 { - return FAULT, nil - } - b := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - a := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - x := AssertStackItem(e.evaluationStack.Pop()).GetBigInteger() - err := pushData(e, WithInOp(x, a, b)) - if err != nil { - return FAULT, err - } - return NONE, nil -} diff --git a/vm/func_array.go b/vm/func_array.go deleted file mode 100755 index d9f0e00f..00000000 --- a/vm/func_array.go +++ /dev/null @@ -1,71 +0,0 @@ -package vm - -func opArraySize(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - arr := AssertStackItem(e.evaluationStack.Pop()).GetArray() - err := pushData(e, len(arr)) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opPack(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - size := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if size < 0 || size > e.evaluationStack.Count() { - return FAULT, nil - } - items := NewStackItems() - for { - if size == 0 { - break - } - items = append(items, AssertStackItem(e.evaluationStack.Pop())) - size-- - } - err := pushData(e, items) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opUnpack(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - arr := AssertStackItem(e.evaluationStack.Pop()).GetArray() - l := len(arr) - for i := l - 1; i >= 0; i-- { - e.evaluationStack.Push(arr[i]) - } - err := pushData(e, l) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opPickItem(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - index := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if index < 0 { - return FAULT, nil - } - items := AssertStackItem(e.evaluationStack.Pop()).GetArray() - if index >= len(items) { - return FAULT, nil - } - err := pushData(e, items[index]) - if err != nil { - return FAULT, err - } - return NONE, nil -} diff --git a/vm/func_bitwise.go b/vm/func_bitwise.go deleted file mode 100755 index d12f368c..00000000 --- a/vm/func_bitwise.go +++ /dev/null @@ -1,29 +0,0 @@ -package vm - -func opInvert(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - x := e.evaluationStack.Pop() - i := AssertStackItem(x).GetBigInteger() - err := pushData(e, i.Not(i)) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opEqual(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := e.evaluationStack.Pop() - x1 := e.evaluationStack.Pop() - b1 := AssertStackItem(x1) - b2 := AssertStackItem(x2) - err := pushData(e, b1.Equals(b2)) - if err != nil { - return FAULT, err - } - return NONE, nil -} diff --git a/vm/func_crypto.go b/vm/func_crypto.go deleted file mode 100755 index 2fb36929..00000000 --- a/vm/func_crypto.go +++ /dev/null @@ -1,107 +0,0 @@ -package vm - -import ( - "crypto/sha1" - "crypto/sha256" - "hash" -) - -func opHash(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - x := AssertStackItem(e.evaluationStack.Pop()).GetByteArray() - err := pushData(e, Hash(x, e)) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opCheckSig(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - pubkey := AssertStackItem(e.evaluationStack.Pop()).GetByteArray() - signature := AssertStackItem(e.evaluationStack.Pop()).GetByteArray() - ver, err := e.crypto.VerifySignature(e.scriptContainer.GetMessage(), signature, pubkey) - err = pushData(e, ver) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opCheckMultiSig(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 4 { - return FAULT, nil - } - n := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if n < 1 { - return FAULT, nil - } - if e.evaluationStack.Count() < n+2 { - return FAULT, nil - } - e.opCount += n - if e.opCount > e.maxSteps { - return FAULT, nil - } - - pubkeys := make([][]byte, n) - for i := 0; i < n; i++ { - pubkeys[i] = AssertStackItem(e.evaluationStack.Pop()).GetByteArray() - } - - m := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if m < 1 || m > n { - return FAULT, nil - } - if e.evaluationStack.Count() < m { - return FAULT, nil - } - - signatures := make([][]byte, m) - for i := 0; i < m; i++ { - signatures[i] = AssertStackItem(e.evaluationStack.Pop()).GetByteArray() - } - - message := e.scriptContainer.GetMessage() - fSuccess := true - - for i, j := 0, 0; fSuccess && i < m && j < n; { - ver, _ := e.crypto.VerifySignature(message, signatures[i], pubkeys[j]) - if ver { - i++ - } - j++ - if m-i > n-j { - fSuccess = false - } - } - err := pushData(e, fSuccess) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func Hash(b []byte, e *ExecutionEngine) []byte { - var sh hash.Hash - var bt []byte - switch e.opCode { - case SHA1: - sh = sha1.New() - sh.Write(b) - bt = sh.Sum(nil) - case SHA256: - sh = sha256.New() - sh.Write(b) - bt = sh.Sum(nil) - case HASH160: - bt = e.crypto.Hash160(b) - case HASH256: - bt = e.crypto.Hash256(b) - } - return bt -} diff --git a/vm/func_flowcontrol.go b/vm/func_flowcontrol.go deleted file mode 100755 index d9d7d364..00000000 --- a/vm/func_flowcontrol.go +++ /dev/null @@ -1,80 +0,0 @@ -package vm - -import ( - "DNA/vm/errors" - "io" - "time" -) - -func opNop(e *ExecutionEngine) (VMState, error) { - time.Sleep(1 * time.Millisecond) - return NONE, nil -} - -func opJmp(e *ExecutionEngine) (VMState, error) { - offset := int(e.context.OpReader.ReadInt16()) - offset = e.context.InstructionPointer + offset - 3 - - if offset < 0 || offset > len(e.context.Script) { - return FAULT, errors.ErrFault - } - fValue := true - if e.opCode > JMP { - s := AssertStackItem(e.evaluationStack.Pop()) - fValue = s.GetBoolean() - if e.opCode == JMPIFNOT { - fValue = !fValue - } - } - if fValue { - e.context.InstructionPointer = offset - } - - return NONE, nil -} - -func opCall(e *ExecutionEngine) (VMState, error) { - e.invocationStack.Push(e.context.Clone()) - e.context.InstructionPointer += 2 - opJmp(e) - return NONE, nil -} - -func opRet(e *ExecutionEngine) (VMState, error) { - if e.invocationStack.Count() < 2 { - return FAULT, nil - } - x := AssertStackItem(e.invocationStack.Pop()) - position := x.GetBigInteger().Int64() - if position < 0 || position > int64(e.context.OpReader.Length()) { - return FAULT, nil - } - e.invocationStack.Push(x) - e.context.OpReader.Seek(position, io.SeekStart) - return NONE, nil -} - -func opAppCall(e *ExecutionEngine) (VMState, error) { - if e.table == nil { - return FAULT, nil - } - script_hash := e.context.OpReader.ReadBytes(20) - script := e.table.GetScript(script_hash) - if script == nil { - return FAULT, nil - } - e.LoadScript(script, false) - return NONE, nil -} - -func opSysCall(e *ExecutionEngine) (VMState, error) { - if e.service == nil { - return FAULT, nil - } - success := e.service.Invoke(e.context.OpReader.ReadVarString(), e) - if success { - return NONE, nil - } else { - return FAULT, nil - } -} diff --git a/vm/func_splice.go b/vm/func_splice.go deleted file mode 100755 index 2c338837..00000000 --- a/vm/func_splice.go +++ /dev/null @@ -1,100 +0,0 @@ -package vm - -func opCat(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := e.evaluationStack.Pop() - x1 := e.evaluationStack.Pop() - b1 := AssertStackItem(x1).GetByteArray() - b2 := AssertStackItem(x2).GetByteArray() - if len(b1) != len(b2) { - return FAULT, nil - } - r := ByteArrZip(b1, b2, CAT) - pushData(e, r) - return NONE, nil -} - -func opSubStr(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 3 { - return FAULT, nil - } - count := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if count < 0 { - return FAULT, nil - } - index := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if index < 0 { - return FAULT, nil - } - x := e.evaluationStack.Pop() - s := AssertStackItem(x).GetByteArray() - l1 := index + count - l2 := len(s) - if l1 > l2 { - return FAULT, nil - } - b := s[index : l2-l1+1] - err := pushData(e, b) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opLeft(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - count := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if count < 0 { - return FAULT, nil - } - x := e.evaluationStack.Pop() - s := AssertStackItem(x).GetByteArray() - if count > len(s) { - return FAULT, nil - } - b := s[:count] - err := pushData(e, b) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opRight(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - count := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if count < 0 { - return FAULT, nil - } - x := e.evaluationStack.Pop() - s := AssertStackItem(x).GetByteArray() - l := len(s) - if count > l { - return FAULT, nil - } - b := s[l-count:] - err := pushData(e, b) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func opSize(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - x := e.evaluationStack.Peek(0) - s := AssertStackItem(x).GetByteArray() - err := pushData(e, len(s)) - if err != nil { - return FAULT, err - } - return NONE, nil -} diff --git a/vm/func_stack.go b/vm/func_stack.go deleted file mode 100755 index c50ef21e..00000000 --- a/vm/func_stack.go +++ /dev/null @@ -1,173 +0,0 @@ -package vm - -func opToAltStack(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - e.altStack.Push(e.evaluationStack.Pop()) - return NONE, nil -} - -func opFromAltStack(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - e.evaluationStack.Push(e.altStack.Pop()) - return NONE, nil -} - -func opXDrop(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - n := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if n < 0 { - return FAULT, nil - } - e.evaluationStack.Remove(n) - return NONE, nil -} - -func opXSwap(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - n := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if n < 0 || n > e.evaluationStack.Count()-1 { - return FAULT, nil - } - e.evaluationStack.Swap(0, n) - return NONE, nil -} - -func opXTuck(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - n := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if n < 0 || n > e.evaluationStack.Count()-1 { - return FAULT, nil - } - e.evaluationStack.Insert(n, e.evaluationStack.Peek(0)) - return NONE, nil -} - -func opDepth(e *ExecutionEngine) (VMState, error) { - pushData(e, e.evaluationStack.Count()) - return NONE, nil -} - -func opDrop(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - e.evaluationStack.Pop() - return NONE, nil -} - -func opDup(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 1 { - return FAULT, nil - } - e.evaluationStack.Push(e.evaluationStack.Peek(0)) - return NONE, nil -} - -func opNip(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := e.evaluationStack.Pop() - e.evaluationStack.Pop() - e.evaluationStack.Push(x2) - return NONE, nil -} - -func opOver(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := e.evaluationStack.Pop() - x1 := e.evaluationStack.Peek(0) - e.evaluationStack.Push(x2) - e.evaluationStack.Push(x1) - return NONE, nil -} - -func opPick(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - n := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if n < 0 { - return FAULT, nil - } - if e.evaluationStack.Count() < n+1 { - return FAULT, nil - } - e.evaluationStack.Push(e.evaluationStack.Peek(n)) - return NONE, nil -} - -func opRoll(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - n := int(AssertStackItem(e.evaluationStack.Pop()).GetBigInteger().Int64()) - if n < 0 { - return FAULT, nil - } - if n == 0 { - return NONE, nil - } - if e.evaluationStack.Count() < n+1 { - return FAULT, nil - } - e.evaluationStack.Push(e.evaluationStack.Remove(n)) - return NONE, nil -} - -func opRot(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 3 { - return FAULT, nil - } - x3 := e.evaluationStack.Pop() - x2 := e.evaluationStack.Pop() - x1 := e.evaluationStack.Pop() - e.evaluationStack.Push(x2) - e.evaluationStack.Push(x3) - e.evaluationStack.Push(x1) - return NONE, nil -} - -func opSwap(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := e.evaluationStack.Pop() - x1 := e.evaluationStack.Pop() - e.evaluationStack.Push(x2) - e.evaluationStack.Push(x1) - return NONE, nil -} - -func opTuck(e *ExecutionEngine) (VMState, error) { - if e.evaluationStack.Count() < 2 { - return FAULT, nil - } - x2 := e.evaluationStack.Pop() - x1 := e.evaluationStack.Pop() - e.evaluationStack.Push(x2) - e.evaluationStack.Push(x1) - e.evaluationStack.Push(x2) - return NONE, nil -} - -func pushData(e *ExecutionEngine, data interface{}) error { - d, err := NewStackItem(data) - if err == nil { - e.evaluationStack.Push(d) - return nil - } - return err -} diff --git a/vm/interfaces/executiontable.go b/vm/interfaces/executiontable.go deleted file mode 100755 index 1e172f0c..00000000 --- a/vm/interfaces/executiontable.go +++ /dev/null @@ -1,5 +0,0 @@ -package interfaces - -type IScriptTable interface { - GetScript(script_hash []byte) ([]byte) -} diff --git a/vm/interfaces/script_container.go b/vm/interfaces/script_container.go deleted file mode 100644 index d0b301cd..00000000 --- a/vm/interfaces/script_container.go +++ /dev/null @@ -1,5 +0,0 @@ -package interfaces - -type IScriptContainer interface { - GetMessage() ([]byte) -} \ No newline at end of file diff --git a/vm/interfaces/signableobject.go b/vm/interfaces/signableobject.go deleted file mode 100755 index 2c71e080..00000000 --- a/vm/interfaces/signableobject.go +++ /dev/null @@ -1,5 +0,0 @@ -package interfaces - -type ISignableObject interface { - GetMessage() ([]byte) -} diff --git a/vm/interop_service.go b/vm/interop_service.go deleted file mode 100644 index 9d155676..00000000 --- a/vm/interop_service.go +++ /dev/null @@ -1,50 +0,0 @@ -package vm - -type InteropService struct { - dictionary map[string]func(*ExecutionEngine) bool -} - -func NewInteropService() *InteropService { - var is InteropService - is.dictionary = make(map[string]func(*ExecutionEngine) bool, 0) - is.Register("System.ScriptEngine.GetScriptContainer", is.GetScriptContainer) - is.Register("System.ScriptEngine.GetExecutingScriptHash", is.GetExecutingScriptHash) - is.Register("System.ScriptEngine.GetCallingScriptHash", is.GetCallingScriptHash) - is.Register("System.ScriptEngine.GetEntryScriptHash", is.GetEntryScriptHash) - return &is -} - -func (is *InteropService) Register(method string, handler func(*ExecutionEngine) bool) bool { - if _, ok := is.dictionary[method]; ok { - return false - } - is.dictionary[method] = handler - return true -} - -func (is *InteropService) Invoke(method string, engine *ExecutionEngine) bool { - if v, ok := is.dictionary[method]; ok { - return v(engine) - } - return false -} - -func (is *InteropService) GetScriptContainer(engine *ExecutionEngine) bool { - engine.evaluationStack.Push(engine.scriptContainer) - return true -} - -func (is *InteropService) GetExecutingScriptHash(engine *ExecutionEngine) bool { - engine.evaluationStack.Push(engine.crypto.Hash160(engine.ExecutingScript())) - return true -} - -func (is *InteropService) GetCallingScriptHash(engine *ExecutionEngine) bool { - engine.evaluationStack.Push(engine.crypto.Hash160(engine.CallingScript())) - return true -} - -func (is *InteropService) GetEntryScriptHash(engine *ExecutionEngine) bool { - engine.evaluationStack.Push(engine.crypto.Hash160(engine.EntryScript())) - return true -} diff --git a/vm/opcode.go b/vm/opcode.go deleted file mode 100755 index 1223715f..00000000 --- a/vm/opcode.go +++ /dev/null @@ -1,120 +0,0 @@ -package vm - -type OpCode byte - -const ( - // Constants - PUSH0 = 0x00 // An empty array of bytes is pushed onto the stack. - PUSHF = PUSH0 - PUSHBYTES1 = 0x01 // 0x01-0x4B The next opcode bytes is data to be pushed onto the stack - PUSHBYTES75 = 0x4B - PUSHDATA1 = 0x4C // The next byte contains the number of bytes to be pushed onto the stack. - PUSHDATA2 = 0x4D // The next two bytes contain the number of bytes to be pushed onto the stack. - PUSHDATA4 = 0x4E // The next four bytes contain the number of bytes to be pushed onto the stack. - PUSHM1 = 0x4F // The number -1 is pushed onto the stack. - PUSH1 = 0x51 // The number 1 is pushed onto the stack. - PUSHT = PUSH1 - PUSH2 = 0x52 // The number 2 is pushed onto the stack. - PUSH3 = 0x53 // The number 3 is pushed onto the stack. - PUSH4 = 0x54 // The number 4 is pushed onto the stack. - PUSH5 = 0x55 // The number 5 is pushed onto the stack. - PUSH6 = 0x56 // The number 6 is pushed onto the stack. - PUSH7 = 0x57 // The number 7 is pushed onto the stack. - PUSH8 = 0x58 // The number 8 is pushed onto the stack. - PUSH9 = 0x59 // The number 9 is pushed onto the stack. - PUSH10 = 0x5A // The number 10 is pushed onto the stack. - PUSH11 = 0x5B // The number 11 is pushed onto the stack. - PUSH12 = 0x5C // The number 12 is pushed onto the stack. - PUSH13 = 0x5D // The number 13 is pushed onto the stack. - PUSH14 = 0x5E // The number 14 is pushed onto the stack. - PUSH15 = 0x5F // The number 15 is pushed onto the stack. - PUSH16 = 0x60 // The number 16 is pushed onto the stack. - - // Flow control - NOP = 0x61 // Does nothing. - JMP = 0x62 - JMPIF = 0x63 - JMPIFNOT = 0x64 - CALL = 0x65 - RET = 0x66 - APPCALL = 0x67 - SYSCALL = 0x68 - - // Stack - TOALTSTACK = 0x6B // Puts the input onto the top of the alt stack. Removes it from the main stack. - FROMALTSTACK = 0x6C // Puts the input onto the top of the main stack. Removes it from the alt stack. - XDROP = 0x6D - XSWAP = 0x72 - XTUCK = 0x73 - DEPTH = 0x74 // Puts the number of stack items onto the stack. - DROP = 0x75 // Removes the top stack item. - DUP = 0x76 // Duplicates the top stack item. - NIP = 0x77 // Removes the second-to-top stack item. - OVER = 0x78 // Copies the second-to-top stack item to the top. - PICK = 0x79 // The item n back in the stack is copied to the top. - ROLL = 0x7A // The item n back in the stack is moved to the top. - ROT = 0x7B // The top three items on the stack are rotated to the left. - SWAP = 0x7C // The top two items on the stack are swapped. - TUCK = 0x7D // The item at the top of the stack is copied and inserted before the second-to-top item. - - // Splice - CAT = 0x7E // Concatenates two strings. - SUBSTR = 0x7F // Returns a section of a string. - LEFT = 0x80 // Keeps only characters left of the specified point in a string. - RIGHT = 0x81 // Keeps only characters right of the specified point in a string. - SIZE = 0x82 // Returns the length of the input string. - - // Bitwise logic - INVERT = 0x83 // Flips all of the bits in the input. - AND = 0x84 // Boolean and between each bit in the inputs. - OR = 0x85 // Boolean or between each bit in the inputs. - XOR = 0x86 // Boolean exclusive or between each bit in the inputs. - EQUAL = 0x87 // Returns 1 if the inputs are exactly equal, 0 otherwise. - //EQUALVERIFY = 0x88 // Same as EQUAL, but runs VERIFY afterward. - //RESERVED1 = 0x89 // Transaction is invalid unless occuring in an unexecuted IF branch - //RESERVED2 = 0x8A // Transaction is invalid unless occuring in an unexecuted IF branch - - // Arithmetic - // Note: Arithmetic inputs are limited to signed 32-bit integers, but may overflow their output. - INC = 0x8B // 1 is added to the input. - DEC = 0x8C // 1 is subtracted from the input. - SAL = 0x8D // The input is multiplied by 2. - SAR = 0x8E // The input is divided by 2. - NEGATE = 0x8F // The sign of the input is flipped. - ABS = 0x90 // The input is made positive. - NOT = 0x91 // If the input is 0 or 1, it is flipped. Otherwise the output will be 0. - NZ = 0x92 // Returns 0 if the input is 0. 1 otherwise. - ADD = 0x93 // a is added to b. - SUB = 0x94 // b is subtracted from a. - MUL = 0x95 // a is multiplied by b. - DIV = 0x96 // a is divided by b. - MOD = 0x97 // Returns the remainder after dividing a by b. - SHL = 0x98 // Shifts a left b bits, preserving sign. - SHR = 0x99 // Shifts a right b bits, preserving sign. - BOOLAND = 0x9A // If both a and b are not 0, the output is 1. Otherwise 0. - BOOLOR = 0x9B // If a or b is not 0, the output is 1. Otherwise 0. - NUMEQUAL = 0x9C // Returns 1 if the numbers are equal, 0 otherwise. - NUMNOTEQUAL = 0x9E // Returns 1 if the numbers are not equal, 0 otherwise. - LT = 0x9F // Returns 1 if a is less than b, 0 otherwise. - GT = 0xA0 // Returns 1 if a is greater than b, 0 otherwise. - LTE = 0xA1 // Returns 1 if a is less than or equal to b, 0 otherwise. - GTE = 0xA2 // Returns 1 if a is greater than or equal to b, 0 otherwise. - MIN = 0xA3 // Returns the smaller of a and b. - MAX = 0xA4 // Returns the larger of a and b. - WITHIN = 0xA5 // Returns 1 if x is within the specified range (left-inclusive), 0 otherwise. - - // Crypto - //RIPEMD160 = 0xA6 // The input is hashed using RIPEMD-160. - SHA1 = 0xA7 // The input is hashed using SHA-1. - SHA256 = 0xA8 // The input is hashed using SHA-256. - HASH160 = 0xA9 - HASH256 = 0xAA - CHECKSIG = 0xAC // The entire transaction's outputs inputs and script (from the most recently-executed CODESEPARATOR to the end) are hashed. The signature used by CHECKSIG must be a valid signature for this hash and public key. If it is 1 is returned 0 otherwise. - CHECKMULTISIG = 0xAE // For each signature and public key pair CHECKSIG is executed. If more public keys than signatures are listed some key/sig pairs can fail. All signatures need to match a public key. If all signatures are valid 1 is returned 0 otherwise. Due to a bug one extra unused value is removed from the stack. - - // Array - ARRAYSIZE = 0xC0 - PACK = 0xC1 - UNPACK = 0xC2 - PICKITEM = 0xC3 -) diff --git a/vm/opcode_exec.go b/vm/opcode_exec.go deleted file mode 100644 index efe3bcb4..00000000 --- a/vm/opcode_exec.go +++ /dev/null @@ -1,119 +0,0 @@ -package vm - -type OpExec struct { - Opcode OpCode - Name string - Exec func(*ExecutionEngine) (VMState, error) -} - -var ( - OpExecList = [256]OpExec{ - // control flow - PUSH0: {PUSH0, "0", opPushData}, - PUSHBYTES1: {PUSHBYTES1, "PUSHBYTES1", opPushData}, - PUSHBYTES75: {PUSHBYTES75, "PUSHBYTES75", opPushData}, - PUSHDATA1: {PUSHDATA1, "PUSHDATA1", opPushData}, - PUSHDATA2: {PUSHDATA2, "PUSHDATA2", opPushData}, - PUSHDATA4: {PUSHDATA4, "PUSHDATA4", opPushData}, - PUSHM1: {PUSHM1, "PUSHM1", opPushData}, - PUSH1: {PUSH1, "1", opPushData}, - PUSH2: {PUSH2, "2", opPushData}, - PUSH3: {PUSH3, "3", opPushData}, - PUSH4: {PUSH4, "4", opPushData}, - PUSH5: {PUSH5, "5", opPushData}, - PUSH6: {PUSH6, "6", opPushData}, - PUSH7: {PUSH7, "7", opPushData}, - PUSH8: {PUSH8, "8", opPushData}, - PUSH9: {PUSH9, "9", opPushData}, - PUSH10: {PUSH10, "10", opPushData}, - PUSH11: {PUSH11, "11", opPushData}, - PUSH12: {PUSH12, "12", opPushData}, - PUSH13: {PUSH13, "13", opPushData}, - PUSH14: {PUSH14, "14", opPushData}, - PUSH15: {PUSH15, "15", opPushData}, - PUSH16: {PUSH16, "16", opPushData}, - - //Control - NOP: {NOP, "NOP", opNop}, - JMP: {JMP, "JMP", opJmp}, - JMPIF: {JMPIF, "JMPIF", opJmp}, - JMPIFNOT: {JMPIFNOT, "JMPIFNOT", opJmp}, - CALL: {CALL, "CALL", opCall}, - RET: {RET, "RET", opRet}, - APPCALL: {APPCALL, "APPCALL", opAppCall}, - SYSCALL: {SYSCALL, "SYSCALL", opSysCall}, - - //Stack ops - TOALTSTACK: {TOALTSTACK, "TOALTSTACK", opToAltStack}, - FROMALTSTACK: {FROMALTSTACK, "FROMALTSTACK", opFromAltStack}, - XDROP: {XDROP, "XDROP", opXDrop}, - XSWAP: {XSWAP, "XSWAPP", opXSwap}, - XTUCK: {XTUCK, "XTUCK", opXTuck}, - DEPTH: {DEPTH, "DEPTH", opDepth}, - DROP: {DROP, "DROP", opDrop}, - DUP: {DUP, "DUP", opDup}, - NIP: {NIP, "NIP", opNip}, - OVER: {OVER, "OVER", opOver}, - PICK: {PICK, "PICK", opPick}, - ROLL: {ROLL, "ROLL", opRoll}, - ROT: {ROT, "ROT", opRot}, - SWAP: {SWAP, "SWAP", opSwap}, - TUCK: {TUCK, "TUCK", opTuck}, - - //Splice - CAT: {CAT, "CAT", opCat}, - SUBSTR: {SUBSTR, "SUBSTR", opSubStr}, - LEFT: {LEFT, "LEFT", opLeft}, - RIGHT: {RIGHT, "RIGHT", opRight}, - SIZE: {SIZE, "SIZE", opSize}, - - //Bitwiase logic - INVERT: {INVERT, "INVERT", opInvert}, - AND: {AND, "AND", opBigIntZip}, - OR: {OR, "OR", opBigIntZip}, - XOR: {XOR, "XOR", opBigIntZip}, - EQUAL: {EQUAL, "EQUAL", opEqual}, - - //Arithmetic - INC: {INC, "INC", opBigInt}, - DEC: {DEC, "DEC", opBigInt}, - SAL: {SAL, "SAL", opBigInt}, - SAR: {SAR, "SAR", opBigInt}, - NEGATE: {NEGATE, "NEGATE", opBigInt}, - ABS: {ABS, "ABS", opBigInt}, - NOT: {NOT, "NOT", opNot}, - NZ: {NZ, "NZ", opNz}, - ADD: {ADD, "ADD", opBigIntZip}, - SUB: {SUB, "SUB", opBigIntZip}, - MUL: {MUL, "MUL", opBigIntZip}, - DIV: {DIV, "DIV", opBigIntZip}, - MOD: {MOD, "MOD", opBigIntZip}, - SHL: {SHL, "SHL", opBigIntZip}, - SHR: {SHR, "SHR", opBigIntZip}, - BOOLAND: {BOOLAND, "BOOLAND", opBoolZip}, - BOOLOR: {BOOLOR, "BOOLOR", opBoolZip}, - NUMEQUAL: {NUMEQUAL, "NUMEQUAL", opBigIntComp}, - NUMNOTEQUAL: {NUMNOTEQUAL, "NUMNOTEQUAL", opBigIntComp}, - LT: {LT, "LT", opBigIntComp}, - GT: {GT, "GT", opBigIntComp}, - LTE: {LTE, "LTE", opBigIntComp}, - GTE: {GTE, "GTE", opBigIntComp}, - MIN: {MIN, "MIN", opBigIntZip}, - MAX: {MAX, "MAX", opBigIntZip}, - WITHIN: {WITHIN, "WITHIN", opWithIn}, - - //Crypto - SHA1: {SHA1, "SHA1", opHash}, - SHA256: {SHA256, "SHA256", opHash}, - HASH160: {HASH160, "HASH160", opHash}, - HASH256: {HASH256, "HASH256", opHash}, - CHECKSIG: {CHECKSIG, "CHECKSIG", opCheckSig}, - CHECKMULTISIG: {CHECKMULTISIG, "CHECKMULTISIG", opCheckMultiSig}, - - //Array - ARRAYSIZE: {ARRAYSIZE, "ARRAYSIZE", opArraySize}, - PACK: {PACK, "PACK", opPack}, - UNPACK: {UNPACK, "UNPACK", opUnpack}, - PICKITEM: {PICKITEM, "PICKITEM", opPickItem}, - } -) diff --git a/vm/types/stackitem.go b/vm/types/stackitem.go deleted file mode 100644 index 77d7f348..00000000 --- a/vm/types/stackitem.go +++ /dev/null @@ -1,14 +0,0 @@ -package types - -import ( - "math/big" -) - -type StackItem interface { - Equals(other StackItem) bool - GetBigInteger() *big.Int - GetBoolean() bool - GetByteArray() []byte - GetInterface() - GetArray() []StackItem -} diff --git a/vm/types/types_test.go b/vm/types/types_test.go deleted file mode 100644 index 262aa4ea..00000000 --- a/vm/types/types_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package types - -import ( - "testing" - "math/big" -) - -func TestTypes(t *testing.T) { - i := NewInteger(big.NewInt(1)) - ba := NewByteArray([]byte{1}) - b := NewBoolean(false) - a1 := NewArray([]StackItem{i}) - //a2 := NewArray([]StackItem{ba}) - t.Log(i.GetByteArray()) - t.Log(ba.GetBoolean()) - t.Log(b.Equals(NewBoolean(false))) - t.Log(a1.Equals(NewArray([]StackItem{NewInteger(big.NewInt(1))}))) -} diff --git a/vm/utils/stack.go b/vm/utils/stack.go deleted file mode 100644 index 71baade5..00000000 --- a/vm/utils/stack.go +++ /dev/null @@ -1,76 +0,0 @@ -package utils - -type RandomAccessStack struct { - Element []interface{} -} - -func NewRandAccessStack() *RandomAccessStack { - var ras RandomAccessStack - ras.Element = make([]interface{}, 0) - return &ras -} - -func (ras *RandomAccessStack) Count() int { - return len(ras.Element) -} - -func (ras *RandomAccessStack) Insert(index int, t interface{}) { - l := len(ras.Element) - if index > l { - return - } - if index == 0 { - ras.Element = append(ras.Element, t) - return - } - - var array = make([]interface{}, 0, l+1) - index = l - index - array = append(array, ras.Element[:index]) - array = append(array, t) - array = append(array, ras.Element[index:]...) - - ras.Element = array -} - -func (ras *RandomAccessStack) Peek(index int) interface{} { - l := len(ras.Element) - if index >= l { - return nil - } - index = l - index - return ras.Element[index-1] -} - -func (ras *RandomAccessStack) Remove(index int) interface{} { - l := len(ras.Element) - if index >= l { - return nil - } - index = l - index - e := ras.Element[index-1] - var si []interface{} - si = append(ras.Element[:index-1], ras.Element[index:]...) - ras.Element = si - return e -} - -func (ras *RandomAccessStack) Set(index int, t interface{}) { - l := len(ras.Element) - if index >= l { - return - } - ras.Element[index] = t -} - -func (ras *RandomAccessStack) Push(t interface{}) { - ras.Insert(0, t) -} - -func (ras *RandomAccessStack) Pop() interface{} { - return ras.Remove(0) -} - -func (ras *RandomAccessStack) Swap(i, j int) { - ras.Element[i], ras.Element[j] = ras.Element[j], ras.Element[i] -} diff --git a/vm/utils/stack_test.go b/vm/utils/stack_test.go deleted file mode 100644 index b161bc4e..00000000 --- a/vm/utils/stack_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package utils - -import ( - "fmt" - "reflect" - "runtime" - "testing" -) - -func assertEqual(t *testing.T, exp, got interface{}) { - res := reflect.DeepEqual(exp, got) - if res == false { - err := fmt.Sprint("Error: expect ", exp, " got ", got) - - _, file, line, _ := runtime.Caller(1) - t.Errorf("%s:%d %s", file, line, err) - } -} - -func TestRandomAccessStack(t *testing.T) { - - var stack = NewRandAccessStack() - assertEqual(t, stack.Count(), 0) - for i := 0; i < 10; i++ { - stack.Push(i) - assertEqual(t, stack.Count(), i+1) - } - - for i := 9; i >= 0; i-- { - elem := stack.Pop() - assertEqual(t, elem.(int), i) - } - assertEqual(t, stack.Count(), 0) - - for i := 0; i < 10; i++ { - stack.Insert(i, i) - assertEqual(t, stack.Peek(i).(int), i) - assertEqual(t, stack.Peek(0).(int), 0) - } - - for i := 0; i < 10; i++ { - stack.Set(i, i+1) - assertEqual(t, stack.Peek(i).(int), i+1) - } - -} From 320285a2e9dae1674499f5beaeeb25403d187a53 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Mon, 25 Sep 2017 16:01:50 +0800 Subject: [PATCH 02/22] Add gilde package support, remove repeat file --- core/store/ChainStore/DataEntryPrefix.go | 32 -- glide.yaml | 6 + vm/ECDsaCrypto.go | 38 --- vm/common.go | 367 ----------------------- vm/doc.go | 14 - vm/func_pushdata.go | 36 --- vm/interfaces/crypto.go | 9 - vm/interfaces/interop.go | 5 - vm/types/array.go | 55 ---- vm/types/boolean.go | 51 ---- vm/types/bytearray.go | 60 ---- vm/types/integer.go | 49 --- vm/types/interop.go | 42 --- vm/utils/vm_reader.go | 108 ------- vm/utils/vmreader_test.go | 42 --- vm/vm_state.go | 12 - 16 files changed, 6 insertions(+), 920 deletions(-) delete mode 100644 core/store/ChainStore/DataEntryPrefix.go delete mode 100644 vm/ECDsaCrypto.go delete mode 100755 vm/common.go delete mode 100644 vm/doc.go delete mode 100755 vm/func_pushdata.go delete mode 100755 vm/interfaces/crypto.go delete mode 100644 vm/interfaces/interop.go delete mode 100644 vm/types/array.go delete mode 100644 vm/types/boolean.go delete mode 100644 vm/types/bytearray.go delete mode 100644 vm/types/integer.go delete mode 100644 vm/types/interop.go delete mode 100755 vm/utils/vm_reader.go delete mode 100755 vm/utils/vmreader_test.go delete mode 100755 vm/vm_state.go diff --git a/core/store/ChainStore/DataEntryPrefix.go b/core/store/ChainStore/DataEntryPrefix.go deleted file mode 100644 index c9ba92d8..00000000 --- a/core/store/ChainStore/DataEntryPrefix.go +++ /dev/null @@ -1,32 +0,0 @@ -package ChainStore - -// DataEntryPrefix -type DataEntryPrefix byte - -const ( - // DATA - DATA_BlockHash DataEntryPrefix = 0x00 - DATA_Header DataEntryPrefix = 0x01 - DATA_Transaction DataEntryPrefix = 0x02 - DATA_Contract DataEntryPrefix = 0x03 - - // INDEX - IX_HeaderHashList DataEntryPrefix = 0x80 - IX_Enrollment DataEntryPrefix = 0x84 - IX_Unspent DataEntryPrefix = 0x90 - IX_Unspent_UTXO DataEntryPrefix = 0x91 - IX_Vote DataEntryPrefix = 0x94 - - // ASSET - ST_Info DataEntryPrefix = 0xc0 - ST_QuantityIssued DataEntryPrefix = 0xc1 - ST_ACCOUNT DataEntryPrefix = 0xc2 - - //SYSTEM - SYS_CurrentBlock DataEntryPrefix = 0x40 - // SYS_CurrentHeader DataEntryPrefix = 0x41 - SYS_CurrentBookKeeper DataEntryPrefix = 0x42 - - //CONFIG - CFG_Version DataEntryPrefix = 0xf0 -) diff --git a/glide.yaml b/glide.yaml index c1afd851..a0c9ee9e 100644 --- a/glide.yaml +++ b/glide.yaml @@ -17,6 +17,12 @@ import: - package: github.com/dnaproject/gopass - package: github.com/gorilla/websocket - package: github.com/pborman/uuid +- package: github.com/ethereum/go-ethereum + subpackages: + - crypto/secp256k1 + - crypto/sha3 + - params +- package: golang.org/x/crypto/ripemd160 ignore: - golang.org/x/sys/unix diff --git a/vm/ECDsaCrypto.go b/vm/ECDsaCrypto.go deleted file mode 100644 index 98dc06cd..00000000 --- a/vm/ECDsaCrypto.go +++ /dev/null @@ -1,38 +0,0 @@ -package vm - -import ( - "DNA/crypto" - . "DNA/errors" - "DNA/common/log" - "errors" -) - - -type ECDsaCrypto struct { -} - -func (c * ECDsaCrypto) Hash160( message []byte ) []byte { - return []byte{} -} - -func (c * ECDsaCrypto) Hash256( message []byte ) []byte { - return []byte{} -} - -func (c * ECDsaCrypto) VerifySignature(message []byte,signature []byte, pubkey []byte) (bool,error) { - - log.Debug("message: %x \n", message) - log.Debug("signature: %x \n", signature) - log.Debug("pubkey: %x \n", pubkey) - - pk,err := crypto.DecodePoint(pubkey) - if err != nil { - return false,NewDetailErr(errors.New("[ECDsaCrypto], crypto.DecodePoint failed."), ErrNoCode, "") - } - - err = crypto.Verify(*pk, message,signature) - if err != nil { - return false,NewDetailErr(errors.New("[ECDsaCrypto], VerifySignature failed."), ErrNoCode, "") - } - return true,nil -} diff --git a/vm/common.go b/vm/common.go deleted file mode 100755 index c89c103d..00000000 --- a/vm/common.go +++ /dev/null @@ -1,367 +0,0 @@ -package vm - -import ( - "DNA/vm/errors" - "DNA/vm/types" - "encoding/binary" - "math/big" - "reflect" -) - -type BigIntSorter []big.Int - -func (c BigIntSorter) Len() int { - return len(c) -} -func (c BigIntSorter) Swap(i, j int) { - if i >= 0 && i < len(c) && j >= 0 && j < len(c) { // Unit Test modify - c[i], c[j] = c[j], c[i] - } -} -func (c BigIntSorter) Less(i, j int) bool { - if i >= 0 && i < len(c) && j >= 0 && j < len(c) { // Unit Test modify - return c[i].Cmp(&c[j]) < 0 - } - - return false -} - -func ToBigInt(data interface{}) *big.Int { - var bi big.Int - switch t := data.(type) { - case int64: - bi.SetInt64(int64(t)) - case int32: - bi.SetInt64(int64(t)) - case int16: - bi.SetInt64(int64(t)) - case int8: - bi.SetInt64(int64(t)) - case int: - bi.SetInt64(int64(t)) - case uint64: - bi.SetUint64(uint64(t)) - case uint32: - bi.SetUint64(uint64(t)) - case uint16: - bi.SetUint64(uint64(t)) - case uint8: - bi.SetUint64(uint64(t)) - case uint: - bi.SetUint64(uint64(t)) - case big.Int: - bi = t - case *big.Int: - bi = *t - } - return &bi -} - -//common func -func SumBigInt(ints []big.Int) big.Int { - sum := big.NewInt(0) - for _, v := range ints { - sum = sum.Add(sum, &v) - } - return *sum -} - -func MinBigInt(ints []big.Int) big.Int{ - minimum := ints[0] - - for _, d := range ints { - if d.Cmp(&minimum) < 0 { - minimum = d - } - } - - return minimum -} - -func MaxBigInt(ints []big.Int) big.Int{ - max := ints[0] - - for _, d := range ints { - if d.Cmp(&max) > 0 { - max = d - } - } - - return max -} - -func MinInt64(datas []int64) int64 { - - var minimum int64 - for i, d := range datas { // Unit Test modify - if i == 0 { - minimum = d - } - if d < minimum { - minimum = d - } - } - - return minimum -} - -func MaxInt64(datas []int64) int64 { - - var maximum int64 - //i := 0 - for i, d := range datas { // Unit Test modify - if i == 0 { - maximum = d - //i++ - } - if d > maximum { - maximum = d - } - } - - return maximum -} - -func Concat(array1 []byte, array2 []byte) []byte { - len := len(array2) - for i := 0; i < len; i++ { - array1 = append(array1, array2[i]) // Unit Test modify - } - - return array1 -} - -func BigIntOp(bi *big.Int, op OpCode) *big.Int { - var nb *big.Int - switch op { - case INC: - nb = bi.Add(bi, big.NewInt(int64(1))) - case DEC: - nb = bi.Sub(bi, big.NewInt(int64(1))) - case SAL: - nb = bi.Lsh(bi, 1) - case SAR: - nb = bi.Rsh(bi, 1) - case NEGATE: - nb = bi.Neg(bi) - case ABS: - nb = bi.Abs(bi) - default: - nb = bi - } - return nb -} - -func AsBool(e interface{}) bool { - if v, ok := e.([]byte); ok { - for _, b := range v { - if b != 0 { - return true - } - } - } - return false -} - -func AsInt64(b []byte) (int64, error) { - if len(b) == 0 { - return 0, nil - } - if len(b) > 8 { - return 0, errors.ErrBadValue - } - - var bs [8]byte - copy(bs[:], b) - - res := binary.LittleEndian.Uint64(bs[:]) - - return int64(res), nil -} - -func ByteArrZip(s1 []byte, s2 []byte, op OpCode) []byte{ - var ns []byte - switch op { - case CAT: - ns = append(s1, s2...) - } - return ns -} - -func BigIntZip(ints1 *big.Int, ints2 *big.Int, op OpCode) *big.Int { - var nb *big.Int - switch op { - case AND: - nb = ints1.And(ints1, ints2) - case OR: - nb = ints1.Or(ints1, ints2) - case XOR: - nb = ints1.Xor(ints1, ints2) - case ADD: - nb = ints1.Add(ints1, ints2) - case SUB: - nb = ints1.Sub(ints1, ints2) - case MUL: - nb = ints1.Mul(ints1, ints2) - case DIV: - nb = ints1.Div(ints1, ints2) - case MOD: - nb = ints1.Mod(ints1, ints2) - case SHL: - nb = ints1.Lsh(ints1, uint(ints2.Int64())) - case SHR: - nb = ints1.Rsh(ints1, uint(ints2.Int64())) - case MIN: - c := ints1.Cmp(ints2) - if c <= 0 { - nb = ints1 - } else { - nb = ints2 - } - case MAX: - c := ints1.Cmp(ints2) - if c <= 0 { - nb = ints2 - } else { - nb = ints1 - } - } - return nb -} - -func BigIntComp(bigint *big.Int, op OpCode) bool { - var nb bool - switch op { - case NZ: - nb = bigint.Cmp(big.NewInt(int64(0))) != 0 - } - return nb -} - -func BigIntMultiComp(ints1 *big.Int, ints2 *big.Int, op OpCode) bool { - var nb bool - switch op { - case NUMEQUAL: - nb = ints1.Cmp(ints2) == 0 - case NUMNOTEQUAL: - nb = ints1.Cmp(ints2) != 0 - case LT: - nb = ints1.Cmp(ints2) < 0 - case GT: - nb = ints1.Cmp(ints2) > 0 - case LTE: - nb = ints1.Cmp(ints2) <= 0 - case GTE: - nb = ints1.Cmp(ints2) >= 0 - } - return nb -} - -func BoolZip(bi1 bool, bi2 bool, op OpCode) bool { - var nb bool - switch op { - case BOOLAND: - nb = bi1 && bi2 - case BOOLOR: - nb = bi1 || bi2 - } - return nb -} - -func BoolArrayOp(bools []bool, op OpCode) []bool { - bls := []bool{} - for _, b := range bools { - var nb bool - - switch op { - case NOT: - nb = !b - default: - nb = b - } - bls = append(bls, nb) - } - - return bls -} - -func IsEqualBytes(b1 []byte, b2 []byte) bool { - len1 := len(b1) - len2 := len(b2) - if len1 != len2 { - return false - } - - for i := 0; i < len1; i++ { - if b1[i] != b2[i] { - return false - } - } - - return true -} - -func IsEqual(v1 interface{}, v2 interface{}) bool { - - if reflect.TypeOf(v1) != reflect.TypeOf(v2) { - return false - } - switch t1 := v1.(type) { - case []byte: - switch t2 := v2.(type) { - case []byte: - return IsEqualBytes(t1, t2) - } - case int8, int16, int32, int64: - if v1 == v2 { - return true - } - return false - default: - return false - } - - return false -} - -func WithInOp(int1 *big.Int, int2 *big.Int, int3 *big.Int) bool { - b1 := BigIntMultiComp(int1, int2, GTE) - b2 := BigIntMultiComp(int1, int3, LT) - return BoolZip(b1, b2, BOOLAND) -} - -func NewStackItems() []types.StackItem { - return make([]types.StackItem, 0) -} - -func NewStackItem(data interface{}) (types.StackItem, error) { - var stackItem types.StackItem - var err error - switch data.(type) { - case int8, int16, int32, int64, int, uint8, uint16, uint32, uint64, *big.Int, big.Int: - stackItem = types.NewInteger(ToBigInt(data)) - case bool: - stackItem = types.NewBoolean(data.(bool)) - case []byte: - stackItem = types.NewByteArray(data.([]byte)) - case []types.StackItem: - stackItem = types.NewArray(data.([]types.StackItem)) - default: - err = errors.ErrBadType - } - return stackItem, err -} - -func AssertExecutionContext(context interface{}) *ExecutionContext { - if c, ok := context.(*ExecutionContext); ok { - return c - } - return nil -} - -func AssertStackItem(stackItem interface{}) types.StackItem { - if s, ok := stackItem.(types.StackItem); ok { - return s - } - return nil -} diff --git a/vm/doc.go b/vm/doc.go deleted file mode 100644 index a465c804..00000000 --- a/vm/doc.go +++ /dev/null @@ -1,14 +0,0 @@ -/* -Copyright 2016 The DNA Dev team - -The go-vm library is free software: you can redistribute it and/or modify -it under the terms of the APACHA License - -package vm implemented a blockchain virtual machine, which provide following main functionaries: - -- opcode VM support the logic of opcode script -- apiservice for external interaction API with triggered by opcode -- crypto for external crypto implementations with triggered by opcode -- - */ -package vm diff --git a/vm/func_pushdata.go b/vm/func_pushdata.go deleted file mode 100755 index 11f452d0..00000000 --- a/vm/func_pushdata.go +++ /dev/null @@ -1,36 +0,0 @@ -package vm - -func opPushData(e *ExecutionEngine) (VMState, error) { - data, err := getPushData(e) - if err != nil { - return FAULT, err - } - err = pushData(e, data) - if err != nil { - return FAULT, err - } - return NONE, nil -} - -func getPushData(e *ExecutionEngine) (interface{}, error) { - var data interface{} - - if e.opCode >= PUSHBYTES1 && e.opCode <= PUSHBYTES75 { - data = e.context.OpReader.ReadBytes(int(e.opCode)) - } - switch e.opCode { - case PUSH0: - data = []byte{0} - case PUSHDATA1: - d, _ := e.context.OpReader.ReadByte() - data = e.context.OpReader.ReadBytes(int(d)) - case PUSHDATA2: - data = e.context.OpReader.ReadBytes(int(e.context.OpReader.ReadUint16())) - case PUSHDATA4: - data = e.context.OpReader.ReadBytes(int(e.context.OpReader.ReadInt32())) - case PUSHM1, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16: - data = int8(e.opCode - PUSH1 + 1) - } - - return data, nil -} diff --git a/vm/interfaces/crypto.go b/vm/interfaces/crypto.go deleted file mode 100755 index 0a35153d..00000000 --- a/vm/interfaces/crypto.go +++ /dev/null @@ -1,9 +0,0 @@ -package interfaces - -type ICrypto interface { - Hash160(message []byte) ([]byte) - - Hash256(message []byte) ([]byte) - - VerifySignature(message []byte,signature []byte, pubkey []byte) (bool,error) -} diff --git a/vm/interfaces/interop.go b/vm/interfaces/interop.go deleted file mode 100644 index a598b4fe..00000000 --- a/vm/interfaces/interop.go +++ /dev/null @@ -1,5 +0,0 @@ -package interfaces - -type IInteropInterface interface { - ToArray() ([]byte) -} diff --git a/vm/types/array.go b/vm/types/array.go deleted file mode 100644 index 0e71c27f..00000000 --- a/vm/types/array.go +++ /dev/null @@ -1,55 +0,0 @@ -package types - -import ( - "math/big" -) - -type Array struct { - _array []StackItem -} - -func NewArray(value []StackItem) *Array{ - var a Array - a._array = value - return &a -} - -func (a *Array) Equals(other StackItem) bool{ - if _, ok := other.(*Array); !ok { - return false - } - a1 := a._array - a2 := other.GetArray() - l1 := len(a1) - l2 := len(a2) - if l1 != l2 { return false } - for i := 0; i max { - return 0 - } - return value -} - -func (r *VmReader) ReadVarString() string{ - bs := r.ReadVarBytes(0X7fffffc7) - return string(bs) - //return Encoding.UTF8.GetString(reader.ReadVarBytes()); -} - diff --git a/vm/utils/vmreader_test.go b/vm/utils/vmreader_test.go deleted file mode 100755 index ee5e6d88..00000000 --- a/vm/utils/vmreader_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package utils - -import ( - "testing" -) - -func TestExampleVmReader(t *testing.T) { - vr := NewVmReader( []byte{0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,0x10,0x11,0x12,0x13} ) - vr1 := NewVmReader( []byte{0x14,0x15,0x16,0x17,0x18,0x19,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa} ) - - t.Log( "NewVmReader() test:", vr ) - t.Log( "NewVmReader() test:", vr.BaseStream ) - - //bt := vr.ReadByte() - //t.Log( "ReadByte() test:", bt,vr.ReadByte() ) - - bb := vr.ReadBytes(4) - t.Log( "ReadBytes() test:", bb ) - - t.Log( "ReadUint16() test:", vr.ReadUint16() ) - - t.Log( "ReadUInt32() test:", vr.ReadUInt32() ) - - t.Log( "ReadUInt64() test:", vr.ReadUInt64() ) - - t.Log( "ReadInt16() test:", vr1.ReadInt16() ) - - t.Log( "ReadInt32() test:", vr1.ReadInt32() ) - - t.Log( "Position() test:", vr1.Position() ) - - t.Log( "Length() test:", vr1.Length() ) - - offset, _ := vr1.Seek( 1, 1 ) - t.Log( "Seek() test:", offset ) - - //bb1 := vr1.ReadVarInt( 999 ) - //t.Log( "ReadVarInt() test:", bb1 ) - - - // output: ok -} diff --git a/vm/vm_state.go b/vm/vm_state.go deleted file mode 100755 index 22a31957..00000000 --- a/vm/vm_state.go +++ /dev/null @@ -1,12 +0,0 @@ -package vm - -type VMState byte - -const ( - NONE VMState = 0 - HALT VMState = 1 << 0 - FAULT VMState = 1 << 1 - BREAK VMState = 1 << 2 - - INSUFFICIENT_RESOURCE VMState = 1 << 4 -) From f39bf850655b979791b4f966c5b0aece35ba3de5 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Mon, 25 Sep 2017 18:29:41 +0800 Subject: [PATCH 03/22] Add ethereum package support --- glide.lock | 6 ++++++ glide.yaml | 1 - vm/evm/contracts.go | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/glide.lock b/glide.lock index 948eabba..4cfa30e5 100644 --- a/glide.lock +++ b/glide.lock @@ -37,4 +37,10 @@ imports: version: f6e8084112db1c3aa9c1e4144f024ab549ea08c1 - name: github.com/whyrusleeping/tar-utils version: e8a5890cfc9d59a203361237581468780d945b6e +- name: github.com/ethereum/go-ethereum + version: 2b4a5f2677fdf0d8a34a2d19fddc203b973dfe02 + subpackages: + - crypto/secp256k1 + - crypto/sha3 + - params testImports: [] diff --git a/glide.yaml b/glide.yaml index a0c9ee9e..252b504f 100644 --- a/glide.yaml +++ b/glide.yaml @@ -22,7 +22,6 @@ import: - crypto/secp256k1 - crypto/sha3 - params -- package: golang.org/x/crypto/ripemd160 ignore: - golang.org/x/sys/unix diff --git a/vm/evm/contracts.go b/vm/evm/contracts.go index 1d7b2f8a..ab8ddbf8 100644 --- a/vm/evm/contracts.go +++ b/vm/evm/contracts.go @@ -6,7 +6,7 @@ import ( . "DNA/vm/evm/common" "DNA/vm/evm/crypto" "crypto/sha256" - "golang.org/x/crypto/ripemd160" + "github.com/golang/crypto/ripemd160" ) type PrecompiledContract interface { From b666b8a1c401e03b2f50b65c7ade6cce6a0097db Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Wed, 27 Sep 2017 17:45:10 +0800 Subject: [PATCH 04/22] change the way of serialize and deserialize byte --- smartcontract/states/statebase.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smartcontract/states/statebase.go b/smartcontract/states/statebase.go index b3255916..bdc1abba 100644 --- a/smartcontract/states/statebase.go +++ b/smartcontract/states/statebase.go @@ -11,16 +11,16 @@ type StateBase struct { } func(stateBase *StateBase)Serialize(w io.Writer) error { - serialization.WriteVarBytes(w, []byte{stateBase.StateVersion}) + serialization.WriteByte(w, stateBase.StateVersion) return nil } func(stateBase *StateBase)Deserialize(r io.Reader) error { - stateVersion, err := serialization.ReadVarBytes(r) + stateVersion, err := serialization.ReadByte(r) if err != nil { return errors.NewDetailErr(err, errors.ErrNoCode, "StateBase StateVersion Deserialize fail.") } - stateBase.StateVersion = stateVersion[0] + stateBase.StateVersion = stateVersion return nil } From 88dcbb8a28f65300564b2d56d73d01086fa8418a Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Wed, 27 Sep 2017 18:11:42 +0800 Subject: [PATCH 05/22] add store.ST_Validator mapping in StatesMap --- smartcontract/states/interfaces.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/smartcontract/states/interfaces.go b/smartcontract/states/interfaces.go index 78ed9256..fcd2816a 100644 --- a/smartcontract/states/interfaces.go +++ b/smartcontract/states/interfaces.go @@ -24,6 +24,7 @@ var ( store.ST_Storage: new(StorageItem), store.ST_ACCOUNT: new(AccountState), store.ST_AssetState: new(AssetState), + store.ST_Validator: new(ValidatorState), } ) @@ -34,4 +35,4 @@ func GetStateValue(prefix store.DataEntryPrefix, data []byte) (IStateValueInterf return nil, err } return state, nil -} \ No newline at end of file +} From 18237850db037456a21978b4491aba8e6e1fa54d Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Thu, 28 Sep 2017 22:04:05 -0500 Subject: [PATCH 06/22] remove pointer, struct only have a attribute. --- smartcontract/states/statebase.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smartcontract/states/statebase.go b/smartcontract/states/statebase.go index bdc1abba..c98a259c 100644 --- a/smartcontract/states/statebase.go +++ b/smartcontract/states/statebase.go @@ -10,12 +10,12 @@ type StateBase struct { StateVersion byte } -func(stateBase *StateBase)Serialize(w io.Writer) error { +func(stateBase StateBase)Serialize(w io.Writer) error { serialization.WriteByte(w, stateBase.StateVersion) return nil } -func(stateBase *StateBase)Deserialize(r io.Reader) error { +func(stateBase StateBase)Deserialize(r io.Reader) error { stateVersion, err := serialization.ReadByte(r) if err != nil { return errors.NewDetailErr(err, errors.ErrNoCode, "StateBase StateVersion Deserialize fail.") From 0c21cfc346e3c928f61441cd937d0af6884ae8eb Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Thu, 28 Sep 2017 22:05:23 -0500 Subject: [PATCH 07/22] remove pointer, it is unnecessary. --- smartcontract/states/assetstate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartcontract/states/assetstate.go b/smartcontract/states/assetstate.go index 54cc9995..61418e0c 100644 --- a/smartcontract/states/assetstate.go +++ b/smartcontract/states/assetstate.go @@ -25,7 +25,7 @@ type AssetState struct { Issuer common.Uint160 Expiration uint32 IsFrozen bool - *StateBase + StateBase } func(assetState *AssetState)Serialize(w io.Writer) error { From 2e70ba850cc67de36c72fd8a6b3eab7202fb3492 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Thu, 28 Sep 2017 22:06:12 -0500 Subject: [PATCH 08/22] remove pointer, it is unnecessary. --- smartcontract/states/contractstate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smartcontract/states/contractstate.go b/smartcontract/states/contractstate.go index d2d418aa..62ea3cdf 100644 --- a/smartcontract/states/contractstate.go +++ b/smartcontract/states/contractstate.go @@ -19,7 +19,7 @@ type ContractState struct { Description string Language types.LangType ProgramHash common.Uint160 - *StateBase + StateBase } func(contractState *ContractState) Serialize(w io.Writer) error { From d724a196bcfcd82ae34fed0d5f8b588263b2ba5e Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 04:44:34 -0500 Subject: [PATCH 09/22] add version control --- smartcontract/states/accountstate.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/smartcontract/states/accountstate.go b/smartcontract/states/accountstate.go index a2e53dac..d95f4dd6 100644 --- a/smartcontract/states/accountstate.go +++ b/smartcontract/states/accountstate.go @@ -8,6 +8,7 @@ import ( ) type AccountState struct { + StateBase ProgramHash common.Uint160 IsFrozen bool Balances map[common.Uint256]common.Fixed64 @@ -22,6 +23,7 @@ func NewAccountState(programHash common.Uint160, balances map[common.Uint256]com } func(accountState *AccountState)Serialize(w io.Writer) error { + accountState.StateBase.Serialize(w) accountState.ProgramHash.Serialize(w) serialization.WriteBool(w, accountState.IsFrozen) serialization.WriteUint64(w, uint64(len(accountState.Balances))) @@ -33,12 +35,22 @@ func(accountState *AccountState)Serialize(w io.Writer) error { } func(accountState *AccountState)Deserialize(r io.Reader) error { + stateBase := new(StateBase) + err := stateBase.Deserialize(r) + if err != nil { + return err + } + accountState.StateBase = *stateBase accountState.ProgramHash.Deserialize(r) isFrozen, err := serialization.ReadBool(r) - if err != nil { return err } + if err != nil { + return err + } accountState.IsFrozen = isFrozen l, err := serialization.ReadUint64(r) - if err != nil { return err } + if err != nil { + return err + } balances := make(map[common.Uint256]common.Fixed64, 0) u := new(common.Uint256) f := new(common.Fixed64) From 1f24eb2736dfb4b746bfcf6dc3e0cb4bc519402a Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 04:45:45 -0500 Subject: [PATCH 10/22] change struct to pointer --- smartcontract/states/statebase.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smartcontract/states/statebase.go b/smartcontract/states/statebase.go index c98a259c..bdc1abba 100644 --- a/smartcontract/states/statebase.go +++ b/smartcontract/states/statebase.go @@ -10,12 +10,12 @@ type StateBase struct { StateVersion byte } -func(stateBase StateBase)Serialize(w io.Writer) error { +func(stateBase *StateBase)Serialize(w io.Writer) error { serialization.WriteByte(w, stateBase.StateVersion) return nil } -func(stateBase StateBase)Deserialize(r io.Reader) error { +func(stateBase *StateBase)Deserialize(r io.Reader) error { stateVersion, err := serialization.ReadByte(r) if err != nil { return errors.NewDetailErr(err, errors.ErrNoCode, "StateBase StateVersion Deserialize fail.") From 6e2c0156cc3a9307dc9f209ac302eb755ffeb49c Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 04:47:02 -0500 Subject: [PATCH 11/22] add verision control --- smartcontract/states/assetstate.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/smartcontract/states/assetstate.go b/smartcontract/states/assetstate.go index 61418e0c..27e55808 100644 --- a/smartcontract/states/assetstate.go +++ b/smartcontract/states/assetstate.go @@ -11,6 +11,7 @@ import ( ) type AssetState struct { + StateBase AssetId common.Uint256 AssetType asset.AssetType Name string @@ -29,6 +30,7 @@ type AssetState struct { } func(assetState *AssetState)Serialize(w io.Writer) error { + assetState.StateBase.Serialize(w) assetState.AssetId.Serialize(w) serialization.WriteVarString(w, assetState.Name) assetState.Amount.Serialize(w) @@ -50,6 +52,12 @@ func(assetState *AssetState)Deserialize(r io.Reader) error { u160 := new(common.Uint160) f := new(common.Fixed64) pubkey := &crypto.PubKey{} + stateBase := new(StateBase) + err := stateBase.Deserialize(r) + if err != nil { + return err + } + assetState.StateBase = *stateBase err := u256.Deserialize(r) if err != nil{ return NewDetailErr(err, ErrNoCode, "AssetState AssetId Deserialize fail.") From cf0a4a8b4b12959cdf8e62e059c750502b2125aa Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 04:48:54 -0500 Subject: [PATCH 12/22] add version control --- smartcontract/states/contractstate.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/smartcontract/states/contractstate.go b/smartcontract/states/contractstate.go index 62ea3cdf..bd0d55e0 100644 --- a/smartcontract/states/contractstate.go +++ b/smartcontract/states/contractstate.go @@ -11,6 +11,7 @@ import ( ) type ContractState struct { + StateBase Code *code.FunctionCode Name string Version string @@ -19,10 +20,10 @@ type ContractState struct { Description string Language types.LangType ProgramHash common.Uint160 - StateBase } func(contractState *ContractState) Serialize(w io.Writer) error { + contractState.StateBase.Serialize(w) err := contractState.Code.Serialize(w) if err != nil { return err @@ -59,8 +60,14 @@ func(contractState *ContractState) Serialize(w io.Writer) error { } func(contractState *ContractState) Deserialize(r io.Reader) error { + stateBase := new(StateBase) + err := stateBase.Deserialize(r) + if err != nil { + return err + } + contractState.StateBase = *stateBase f := new(code.FunctionCode) - err := f.Deserialize(r) + err = f.Deserialize(r) if err != nil { return NewDetailErr(err, ErrNoCode, "ContractState Code Deserialize fail.") } From 24c6c90c627ce756518bb1d40d42b662df7646d9 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 04:50:08 -0500 Subject: [PATCH 13/22] add version control --- smartcontract/states/storageitem.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/smartcontract/states/storageitem.go b/smartcontract/states/storageitem.go index c1f116b5..e8f7ce5c 100644 --- a/smartcontract/states/storageitem.go +++ b/smartcontract/states/storageitem.go @@ -8,8 +8,8 @@ import ( ) type StorageItem struct { + StateBase Value []byte - *StateBase } func NewStorageItem(value []byte) *StorageItem { @@ -19,11 +19,18 @@ func NewStorageItem(value []byte) *StorageItem { } func(storageItem *StorageItem)Serialize(w io.Writer) error { + storageItem.StateBase.Serialize(w) serialization.WriteVarBytes(w, storageItem.Value) return nil } func(storageItem *StorageItem)Deserialize(r io.Reader) error { + stateBase := new(StateBase) + err := stateBase.Deserialize(r) + if err != nil { + return err + } + storageItem.StateBase = *stateBase value, err := serialization.ReadVarBytes(r) if err != nil { return NewDetailErr(err, ErrNoCode, "ContractState Code Deserialize fail.") @@ -36,4 +43,4 @@ func(storageItem *StorageItem) ToArray() []byte { b := new(bytes.Buffer) storageItem.Serialize(b) return b.Bytes() -} \ No newline at end of file +} From 418aece5028bb65e913d0068dab79375a2f3b411 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 04:51:10 -0500 Subject: [PATCH 14/22] add version control --- smartcontract/states/validatorstate.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/smartcontract/states/validatorstate.go b/smartcontract/states/validatorstate.go index 9e764ddf..cb7474c6 100644 --- a/smartcontract/states/validatorstate.go +++ b/smartcontract/states/validatorstate.go @@ -7,16 +7,19 @@ import ( ) type ValidatorState struct { + StateBase PublicKey *crypto.PubKey } func(v *ValidatorState) Serialize(w io.Writer) error { + v.StateBase.Serialize(w) v.PublicKey.Serialize(w) return nil } func(v *ValidatorState)Deserialize(r io.Reader) error { + p := new(crypto.PubKey) p.DeSerialize(r) v.PublicKey = p @@ -24,7 +27,17 @@ func(v *ValidatorState)Deserialize(r io.Reader) error { } func(v *ValidatorState) ToArray() []byte { - b := new(bytes.Buffer) - v.Serialize(b) - return b.Bytes() -} \ No newline at end of file + stateBase := new(StateBase) + err := stateBase.Deserialize(r) + if err != nil { + return err + } + v.StateBase = *stateBase + p := new(crypto.PubKey) + err = p.DeSerialize(r) + if err != nil { + return err + } + v.PublicKey = p + return nil +} From 0e464d7020714b7870ea79e1eb7a9780d753fead Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 04:53:28 -0500 Subject: [PATCH 15/22] remove repeated statebase and err --- smartcontract/states/assetstate.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/smartcontract/states/assetstate.go b/smartcontract/states/assetstate.go index 27e55808..421d4e3d 100644 --- a/smartcontract/states/assetstate.go +++ b/smartcontract/states/assetstate.go @@ -26,7 +26,6 @@ type AssetState struct { Issuer common.Uint160 Expiration uint32 IsFrozen bool - StateBase } func(assetState *AssetState)Serialize(w io.Writer) error { @@ -58,7 +57,7 @@ func(assetState *AssetState)Deserialize(r io.Reader) error { return err } assetState.StateBase = *stateBase - err := u256.Deserialize(r) + err = u256.Deserialize(r) if err != nil{ return NewDetailErr(err, ErrNoCode, "AssetState AssetId Deserialize fail.") } From 70d7ecec71fafb020821e5792dc0b140aa057fcc Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 04:58:23 -0500 Subject: [PATCH 16/22] add ToArray() function --- smartcontract/states/validatorstate.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/smartcontract/states/validatorstate.go b/smartcontract/states/validatorstate.go index cb7474c6..105942b3 100644 --- a/smartcontract/states/validatorstate.go +++ b/smartcontract/states/validatorstate.go @@ -41,3 +41,9 @@ func(v *ValidatorState) ToArray() []byte { v.PublicKey = p return nil } + +func(v *ValidatorState) ToArray() []byte { + b := new(bytes.Buffer) + v.Serialize(b) + return b.Bytes() +} From c6758b21bbb9349c8d8e1aeee1069ce02b7c3ac9 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 29 Sep 2017 05:06:19 -0500 Subject: [PATCH 17/22] correct the error code --- smartcontract/states/validatorstate.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/smartcontract/states/validatorstate.go b/smartcontract/states/validatorstate.go index 105942b3..b7c7099d 100644 --- a/smartcontract/states/validatorstate.go +++ b/smartcontract/states/validatorstate.go @@ -19,14 +19,6 @@ func(v *ValidatorState) Serialize(w io.Writer) error { func(v *ValidatorState)Deserialize(r io.Reader) error { - - p := new(crypto.PubKey) - p.DeSerialize(r) - v.PublicKey = p - return nil -} - -func(v *ValidatorState) ToArray() []byte { stateBase := new(StateBase) err := stateBase.Deserialize(r) if err != nil { @@ -47,3 +39,4 @@ func(v *ValidatorState) ToArray() []byte { v.Serialize(b) return b.Bytes() } + From f30a4a998b7ed258e053a4c7b85353dec01c0f7d Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Thu, 2 Nov 2017 10:59:17 +0800 Subject: [PATCH 18/22] =?UTF-8?q?fix=20bug=20that=20forget=20commit=20cach?= =?UTF-8?q?e=20data=20to=20level=20db=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/store/ChainStore/ChainStore.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/store/ChainStore/ChainStore.go b/core/store/ChainStore/ChainStore.go index f6b8a6be..2b7119a0 100644 --- a/core/store/ChainStore/ChainStore.go +++ b/core/store/ChainStore/ChainStore.go @@ -1160,6 +1160,11 @@ func (bd *ChainStore) persist(b *Block) error { // BATCH PUT VALUE bd.st.BatchPut(currentBlockKey.Bytes(), currentBlock.Bytes()) + err = dbCache.Commit() + if err != nil { + return err + } + err = bd.st.BatchCommit() if err != nil { From 32f67d76f16c84c1af4c7e64c7e875d1913f0b96 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 3 Nov 2017 14:23:02 +0800 Subject: [PATCH 19/22] add get an byte array of value support --- vm/avm/func_validate.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/vm/avm/func_validate.go b/vm/avm/func_validate.go index bf204236..05c16bde 100644 --- a/vm/avm/func_validate.go +++ b/vm/avm/func_validate.go @@ -195,8 +195,10 @@ func validatePickItem(e *ExecutionEngine) error { return ErrBadValue } stackItem := item.GetStackItem() - if _, ok := stackItem.(*types.Array); !ok { - return ErrNotArray + if _, ok1 := stackItem.(*types.Array); !ok1 { + if _, ok2 := stackItem.(*types.ByteArray); !ok2 { + return ErrNotArray + } } if index >= len(stackItem.GetArray()) { return ErrOverMaxArraySize @@ -212,22 +214,17 @@ func validatorSetItem(e *ExecutionEngine) error { if newItem == nil { return ErrBadValue } - fmt.Println("new item:", newItem) index := PeekNInt(1, e) if index < 0 { return ErrBadValue } - fmt.Println("index:", index) arrItem := PeekN(2, e) if arrItem == nil { return ErrBadValue } - fmt.Println("arrItem:", arrItem) item := arrItem.GetStackItem() if _, ok := item.(*types.Array); !ok { if _, ok := item.(*types.ByteArray); ok { - fmt.Println("item:", item.GetByteArray()) - fmt.Println("item length:", len(item.GetByteArray())) l := len(item.GetByteArray()) if index >= l { return ErrOverMaxArraySize @@ -239,8 +236,6 @@ func validatorSetItem(e *ExecutionEngine) error { return ErrBadValue } }else { - fmt.Println("item:", item.GetArray()) - fmt.Println("item length:", len(item.GetArray())) if index >= len(item.GetArray()) { return ErrOverMaxArraySize } From 7dff32fb50742024bf918a41a824400e1f46d024 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 3 Nov 2017 14:25:38 +0800 Subject: [PATCH 20/22] Remove redundant fmt print import. --- vm/avm/func_validate.go | 1 - 1 file changed, 1 deletion(-) diff --git a/vm/avm/func_validate.go b/vm/avm/func_validate.go index 05c16bde..dcfc37cf 100644 --- a/vm/avm/func_validate.go +++ b/vm/avm/func_validate.go @@ -5,7 +5,6 @@ import ( "bytes" "encoding/binary" "DNA/vm/avm/types" - "fmt" ) func validatorPushData4(e *ExecutionEngine) error { From 0e0959e4f26cbedf6c276daaf2013330da175e23 Mon Sep 17 00:00:00 2001 From: tanyuan <1067598718@qq.com> Date: Fri, 3 Nov 2017 14:47:26 +0800 Subject: [PATCH 21/22] fig some bug when get an byte array of value. --- vm/avm/func_array.go | 10 ++++++++-- vm/avm/func_validate.go | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/vm/avm/func_array.go b/vm/avm/func_array.go index e08f053d..e2edd895 100644 --- a/vm/avm/func_array.go +++ b/vm/avm/func_array.go @@ -37,8 +37,14 @@ func opUnpack(e *ExecutionEngine) (VMState, error) { func opPickItem(e *ExecutionEngine) (VMState, error) { index := PopInt(e) - items := PopArray(e) - PushData(e, items[index]) + itemArr := PopStackItem(e) + if _, ok := itemArr.(*types.Array); ok { + items := itemArr.GetArray() + PushData(e, items[index]) + }else { + items := itemArr.GetByteArray() + PushData(e, items[index]) + } return NONE, nil } diff --git a/vm/avm/func_validate.go b/vm/avm/func_validate.go index dcfc37cf..78dd2926 100644 --- a/vm/avm/func_validate.go +++ b/vm/avm/func_validate.go @@ -194,14 +194,20 @@ func validatePickItem(e *ExecutionEngine) error { return ErrBadValue } stackItem := item.GetStackItem() - if _, ok1 := stackItem.(*types.Array); !ok1 { - if _, ok2 := stackItem.(*types.ByteArray); !ok2 { + if _, ok := stackItem.(*types.Array); !ok { + if _, ok := stackItem.(*types.ByteArray); !ok { return ErrNotArray + } else { + if index >= len(stackItem.GetByteArray()) { + return ErrOverMaxArraySize + } + } + } else { + if index >= len(stackItem.GetArray()) { + return ErrOverMaxArraySize } } - if index >= len(stackItem.GetArray()) { - return ErrOverMaxArraySize - } + return nil } @@ -232,7 +238,7 @@ func validatorSetItem(e *ExecutionEngine) error { return ErrBadValue } } else { - return ErrBadValue + return ErrNotArray } }else { if index >= len(item.GetArray()) { From fb21d1703e03e2270bb0ac37f848418fb03b32e1 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Wed, 15 Nov 2017 17:49:01 +0800 Subject: [PATCH 22/22] Record restful interface Signed-off-by: Xiang Fu --- net/httprestful/common/custom.go | 72 +++++++++++++++++++++++++------ net/httprestful/restful/server.go | 15 +++++-- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/net/httprestful/common/custom.go b/net/httprestful/common/custom.go index ec7bf356..aac9be57 100644 --- a/net/httprestful/common/custom.go +++ b/net/httprestful/common/custom.go @@ -2,7 +2,9 @@ package common import ( . "DNA/common" + "DNA/core/ledger" tx "DNA/core/transaction" + "DNA/core/transaction/payload" . "DNA/errors" . "DNA/net/httpjsonrpc" Err "DNA/net/httprestful/error" @@ -13,6 +15,19 @@ import ( const AttributeMaxLen = 252 +type Data struct { + Algrithem string `json:Algrithem` + Hash string `json:Hash` + Signature string `json:Signature` + Text string `json:Text` +} +type RecordData struct { + CAkey string `json:CAkey` + Data Data `json:Data` + SeqNo string `json:SeqNo` + Timestamp float64 `json:Timestamp` +} + //record func getRecordData(cmd map[string]interface{}) ([]byte, int64) { if raw, ok := cmd["Raw"].(string); ok && raw == "1" { @@ -26,18 +41,6 @@ func getRecordData(cmd map[string]interface{}) ([]byte, int64) { } return bys, Err.SUCCESS } - type Data struct { - Algrithem string `json:Algrithem` - Hash string `json:Hash` - Signature string `json:Signature` - Text string `json:Text` - } - type RecordData struct { - CAkey string `json:CAkey` - Data Data `json:Data` - SeqNo string `json:SeqNo` - Timestamp float64 `json:Timestamp` - } tmp := &RecordData{} reqRecordData, ok := cmd["RecordData"].(map[string]interface{}) @@ -119,7 +122,16 @@ func SendRecord(cmd map[string]interface{}) map[string]interface{} { func SendRecordTransaction(cmd map[string]interface{}) map[string]interface{} { resp := ResponsePack(Err.SUCCESS) var recordData []byte - recordData, resp["Error"] = getRecordData(cmd) + reqRecordData, ok := cmd["RecordData"].(map[string]interface{}) + if !ok { + resp["Error"] = Err.INVALID_PARAMS + return resp + } + recordData, err := json.Marshal(reqRecordData) + if err != nil { + resp["Error"] = Err.INVALID_PARAMS + return resp + } if recordData == nil { return resp } @@ -134,3 +146,37 @@ func SendRecordTransaction(cmd map[string]interface{}) map[string]interface{} { } return resp } + +func GetRecordByHash(cmd map[string]interface{}) map[string]interface{} { + resp := ResponsePack(Err.SUCCESS) + + str := cmd["Hash"].(string) + bys, err := HexToBytesReverse(str) + if err != nil { + resp["Error"] = Err.INVALID_PARAMS + return resp + } + var hash Uint256 + err = hash.Deserialize(bytes.NewReader(bys)) + if err != nil { + resp["Error"] = Err.INVALID_TRANSACTION + return resp + } + tx, err := ledger.DefaultLedger.Store.GetTransaction(hash) + if err != nil { + resp["Error"] = Err.UNKNOWN_TRANSACTION + return resp + } + recordinfo := tx.Payload.(*payload.Record) + if recordinfo.RecordType != "record" { + resp["Error"] = Err.INVALID_PARAMS + return resp + } + tmp := &RecordData{} + if err := json.Unmarshal(recordinfo.RecordData, tmp); err != nil { + resp["Error"] = Err.INVALID_PARAMS + return resp + } + resp["Result"] = tmp + return resp +} diff --git a/net/httprestful/restful/server.go b/net/httprestful/restful/server.go index 4c1a1241..3ffbdf6f 100644 --- a/net/httprestful/restful/server.go +++ b/net/httprestful/restful/server.go @@ -56,6 +56,7 @@ const ( Api_WebsocketState = "/api/v1/config/websocket/state" Api_Restart = "/api/v1/restart" Api_GetContract = "/api/v1/contract/:hash" + Api_GetRecordByHash = "/api/v1/custom/record/:hash" ) func InitRestServer(checkAccessToken func(string, string) (string, int64, interface{})) ApiServer { @@ -155,6 +156,7 @@ func (rt *restServer) registryMethod() { Api_NoticeServerUrl: {name: "getnoticeserverurl", handler: GetNoticeServerUrl}, Api_Restart: {name: "restart", handler: rt.Restart}, Api_GetStateUpdate: {name: "getstateupdate", handler: GetStateUpdate}, + Api_GetRecordByHash: {name: "getrecord", handler: GetRecordByHash}, } sendRawTransaction := func(cmd map[string]interface{}) map[string]interface{} { @@ -169,7 +171,7 @@ func (rt *restServer) registryMethod() { } postMethodMap := map[string]Action{ Api_SendRawTx: {name: "sendrawtransaction", handler: sendRawTransaction}, - Api_SendRcdTxByTrans: {name: "sendrecord", handler: SendRecord}, + Api_SendRcdTxByTrans: {name: "sendrecord", handler: SendRecordTransaction}, Api_OauthServerUrl: {name: "setoauthserverurl", handler: SetOauthServerUrl}, Api_NoticeServerUrl: {name: "setnoticeserverurl", handler: SetNoticeServerUrl}, Api_NoticeServerState: {name: "setpostblock", handler: SetPushBlockFlag}, @@ -206,6 +208,8 @@ func (rt *restServer) getPath(url string) string { return Api_Getasset } else if strings.Contains(url, strings.TrimRight(Api_GetStateUpdate, ":namespace/:key")) { return Api_GetStateUpdate + } else if strings.Contains(url, strings.TrimRight(Api_GetRecordByHash, ":namespace/:hash")) { + return Api_GetRecordByHash } return url } @@ -273,6 +277,9 @@ func (rt *restServer) getParams(r *http.Request, url string, req map[string]inte req["Namespace"] = getParam(r, "namespace") req["Key"] = getParam(r, "key") break + case Api_GetRecordByHash: + req["Hash"] = getParam(r, "hash") + break case Api_OauthServerUrl: case Api_NoticeServerUrl: case Api_NoticeServerState: @@ -284,7 +291,7 @@ func (rt *restServer) getParams(r *http.Request, url string, req map[string]inte } func (rt *restServer) initGetHandler() { - for k, _ := range rt.getMap { + for k := range rt.getMap { rt.router.Get(k, func(w http.ResponseWriter, r *http.Request) { var req = make(map[string]interface{}) @@ -313,7 +320,7 @@ func (rt *restServer) initGetHandler() { } } func (rt *restServer) initPostHandler() { - for k, _ := range rt.postMap { + for k := range rt.postMap { rt.router.Post(k, func(w http.ResponseWriter, r *http.Request) { body, _ := ioutil.ReadAll(r.Body) @@ -349,7 +356,7 @@ func (rt *restServer) initPostHandler() { }) } //Options - for k, _ := range rt.postMap { + for k := range rt.postMap { rt.router.Options(k, func(w http.ResponseWriter, r *http.Request) { rt.write(w, []byte{}) })