From 19c21452c974347173885fe5ed27655c82c278ff Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 17:45:37 +0300 Subject: [PATCH 01/35] interop/account: update and extend documentation --- pkg/interop/account/account.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/pkg/interop/account/account.go b/pkg/interop/account/account.go index c2e4cb19c..c18172eb7 100644 --- a/pkg/interop/account/account.go +++ b/pkg/interop/account/account.go @@ -1,23 +1,36 @@ +/* +Package account provides getter functions for Account interop structure. +To use these functions you need to get an Account first via blockchain.GetAccount +call. +*/ package account -// Package account provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Account stubs a NEO account type. +// Account represents NEO account type that is used in interop functions, it's +// an opaque data structure that you can get data from only using functions from +// this package. It's similar in function to the Account class in the Neo .net +// framework. type Account struct{} -// GetScriptHash returns the script hash of the given account. +// GetScriptHash returns the script hash of the given Account (20 bytes in BE +// representation). It uses `Neo.Account.GetBalance` syscall internally. func GetScriptHash(a Account) []byte { return nil } -// GetVotes returns the votes of the given account which should be a slice of -// public key raw bytes. +// GetVotes returns current votes of the given account represented as a slice of +// public keys. Keys are serialized into byte slices in their compressed form (33 +// bytes long each). This function uses `Neo.Account.GetVotes` syscall +// internally. func GetVotes(a Account) [][]byte { return nil } -// GetBalance returns the balance of for the given account and asset id. +// GetBalance returns current balance of the given asset (by its ID, 256 bit +// hash in BE form) for the given account. Only native UTXO assets can be +// queiried via this function, for NEP-5 ones use respective contract calls. +// The value returned is represented as an integer with original value multiplied +// by 10⁸ so you can work with fractional parts of the balance too. This function +// uses `Neo.Account.GetBalance` syscall internally. func GetBalance(a Account, assetID []byte) int { return 0 } From 7c1a54b7258776015ba493bba583d3f4befc4154 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 18:16:16 +0300 Subject: [PATCH 02/35] account: add missing IsStandard interop function There is a Neo.Account.IsStandard syscall, but we didn't have a wrapper for it. --- pkg/interop/account/account.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/interop/account/account.go b/pkg/interop/account/account.go index c18172eb7..f0595eec0 100644 --- a/pkg/interop/account/account.go +++ b/pkg/interop/account/account.go @@ -34,3 +34,10 @@ func GetVotes(a Account) [][]byte { func GetBalance(a Account, assetID []byte) int { return 0 } + +// IsStandard checks whether given account uses standard (CHECKSIG or +// CHECKMULTISIG) contract. It only works for deployed contracts and uses +// `Neo.Account.IsStandard` syscall internally. +func IsStandard(a Account) bool { + return false +} From 41337732be52c482af380884a40b844665ebecd2 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 18:50:37 +0300 Subject: [PATCH 03/35] compiler: add support for account syscalls Turns out, they never functioned correctly. --- pkg/compiler/syscall.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 633335b4b..a573fb530 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -1,6 +1,12 @@ package compiler var syscalls = map[string]map[string]string{ + "account": { + "GetBalance": "Neo.Account.GetBalance", + "GetScriptHash": "Neo.Account.GetScriptHash", + "GetVotes": "Neo.Account.GetVotes", + "IsStandard": "Neo.Account.IsStandard", + }, "crypto": { "ECDsaVerify": "Neo.Crypto.ECDsaVerify", }, From ff583a10a59b15f4367c0802bb131861effa99aa Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 21:13:28 +0300 Subject: [PATCH 04/35] asset: update documentation and fix Create/Renew Both Create and Renew have things returned from them. --- pkg/interop/asset/asset.go | 76 ++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/pkg/interop/asset/asset.go b/pkg/interop/asset/asset.go index 3c84ea367..01547f64c 100644 --- a/pkg/interop/asset/asset.go +++ b/pkg/interop/asset/asset.go @@ -1,53 +1,97 @@ +/* +Package asset provides functions to work with regular UTXO assets (like NEO or GAS). +Mostly these are getters for Asset structure, but you can also create new assets +and renew them (although it's recommended to use NEP-5 standard for new tokens). +*/ package asset -// Package asset provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Asset stubs a NEO asset type. +// Asset represents NEO asset type that is used in interop functions, it's +// an opaque data structure that you can get data from only using functions from +// this package. It's similar in function to the Asset class in the Neo .net +// framework. To be able to use it you either need to get an existing Asset via +// blockchain.GetAsset function or create a new one via Create. type Asset struct{} -// GetAssetID returns the id of the given asset. +// GetAssetID returns ID (256-bit ID of Register transaction for this asset in BE +// representation) of the given asset. It uses `Neo.Asset.GetAssetId` syscall +// internally. func GetAssetID(a Asset) []byte { return nil } -// GetAssetType returns the type of the given asset. +// GetAssetType returns type of the given asset as a byte value. The value +// returned can be interpreted as a bit field with the following meaning: +// CreditFlag = 0x40 +// DutyFlag = 0x80 +// SystemShare = 0x00 +// SystemCoin = 0x01 +// Currency = 0x08 +// Share = DutyFlag | 0x10 +// Invoice = DutyFlag | 0x18 +// Token = CreditFlag | 0x20 +// It uses `Neo.Asset.GetAssetType` syscall internally. func GetAssetType(a Asset) byte { return 0x00 } -// GetAmount returns the amount of the given asset. +// GetAmount returns the total amount of the given asset as an integer +// multiplied by 10⁸. This value is the maximum possible circulating quantity of +// Asset. The function uses `Neo.Asset.GetAmount` syscall internally. func GetAmount(a Asset) int { return 0 } -// GetAvailable returns the available of the given asset. +// GetAvailable returns the amount of Asset currently available on the +// blockchain. It uses the same encoding as the result of GetAmount and its +// value can never exceed the value returned by GetAmount. This function uses +// `Neo.Asset.GetAvailable` syscall internally. func GetAvailable(a Asset) int { return 0 } -// GetPrecision returns the precision of the given asset. +// GetPrecision returns precision of the given Asset. It uses +// `Neo.Asset.GetPrecision` syscall internally. func GetPrecision(a Asset) byte { return 0x00 } -// GetOwner returns the owner of the given asset. +// GetOwner returns the owner of the given Asset. It's represented as a +// serialized (in compressed form) public key (33 bytes long). This function +// uses `Neo.Asset.GetOwner` syscall internally. func GetOwner(a Asset) []byte { return nil } -// GetAdmin returns the admin of the given asset. +// GetAdmin returns the admin of the given Asset represented as a 160 bit hash +// in BE form (contract script hash). Admin can modify attributes of this Asset. +// This function uses `Neo.Asset.GetAdmin` syscall internally. func GetAdmin(a Asset) []byte { return nil } -// GetIssuer returns the issuer of the given asset. +// GetIssuer returns the issuer of the given Asset represented as a 160 bit hash +// in BE form (contract script hash). Issuer can issue new tokens for this Asset. +// This function uses `Neo.Asset.GetIssuer` syscall internally. func GetIssuer(a Asset) []byte { return nil } -// Create registers a new asset on the blockchain. -func Create(assetType byte, name string, amount int, precision byte, owner, admin, issuer []byte) {} +// Create registers a new asset on the blockchain (similar to old Register +// transaction). `assetType` parameter has the same set of possible values as +// GetAssetType result, `amount` must be multiplied by 10⁸, `precision` limits +// the smallest possible amount of new Asset to 10⁻ⁿ (where n is precision which +// can't exceed 8), `owner` is a public key of the owner in compressed serialized +// form (33 bytes), `admin` and `issuer` should be represented as 20-byte slices +// storing 160-bit hash in BE form. Created Asset is set to expire in one year, +// so you need to renew it in time. If successful, this function returns a new +// Asset. It uses `Neo.Asset.Create` syscall internally. +func Create(assetType byte, name string, amount int, precision byte, owner, admin, issuer []byte) Asset { + return Asset{} +} -// Renew renews the existence of an asset by the given years. -func Renew(asset Asset, years int) {} +// Renew renews (make available for use) existing asset by the specified number +// of years. It returns the last block number when this asset will be active. +// It uses `Neo.Asset.Renew` syscall internally. +func Renew(asset Asset, years int) int { + return 0 +} From 6fac273d5d951d26135f18ebb442db94cff057d6 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 21:16:53 +0300 Subject: [PATCH 05/35] compiler: add missing asset syscalls, sort them --- pkg/compiler/syscall.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index a573fb530..4ac9b1c37 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -62,10 +62,15 @@ var syscalls = map[string]map[string]string{ "GetScript": "Neo.Transaction.GetScript", }, "asset": { + "Create": "Neo.Asset.Create", + "GetAdmin": "Neo.Asset.GetAdmin", + "GetAmount": "Neo.Asset.GetAmount", "GetAssetID": "Neo.Asset.GetAssetID", "GetAssetType": "Neo.Asset.GetAssetType", - "GetAmount": "Neo.Asset.GetAmount", - "Create": "Neo.Asset.Create", + "GetAvailable": "Neo.Asset.GetAvailable", + "GetIssuer": "Neo.Asset.GetIssuer", + "GetOwner": "Neo.Asset.GetOwner", + "GetPrecision": "Neo.Asset.GetPrecision", "Renew": "Neo.Asset.Renew", }, "contract": { From 60bea7e224464c95ae608b4bc59b4ab56da6685a Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 21:29:05 +0300 Subject: [PATCH 06/35] interop/attribute: update documentation --- pkg/interop/attribute/attribute.go | 60 +++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/pkg/interop/attribute/attribute.go b/pkg/interop/attribute/attribute.go index 6147b5be2..b4c0175c4 100644 --- a/pkg/interop/attribute/attribute.go +++ b/pkg/interop/attribute/attribute.go @@ -1,17 +1,65 @@ +/* +Package attribute provides getters for transaction attributes. +*/ package attribute -// Package attribute provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Attribute stubs a NEO transaction attribute type. +// Attribute represents transaction attribute in Neo, it's an opaque data +// structure that you can get data from only using functions from this package. +// It's similar in function to the TransactionAttribute class in the Neo .net +// framework. To use it you need to get is first using transaction.GetAttributes. type Attribute struct{} -// GetUsage returns the usage of the given attribute. +// GetUsage returns the Usage field of the given attribute. It is an enumeration +// with the following possible values: +// ContractHash = 0x00 +// ECDH02 = 0x02 +// ECDH03 = 0x03 +// Script = 0x20 +// Vote = 0x30 +// CertURL = 0x80 +// DescriptionURL = 0x81 +// Description = 0x90 +// +// Hash1 = 0xa1 +// Hash2 = 0xa2 +// Hash3 = 0xa3 +// Hash4 = 0xa4 +// Hash5 = 0xa5 +// Hash6 = 0xa6 +// Hash7 = 0xa7 +// Hash8 = 0xa8 +// Hash9 = 0xa9 +// Hash10 = 0xaa +// Hash11 = 0xab +// Hash12 = 0xac +// Hash13 = 0xad +// Hash14 = 0xae +// Hash15 = 0xaf +// +// Remark = 0xf0 +// Remark1 = 0xf1 +// Remark2 = 0xf2 +// Remark3 = 0xf3 +// Remark4 = 0xf4 +// Remark5 = 0xf5 +// Remark6 = 0xf6 +// Remark7 = 0xf7 +// Remark8 = 0xf8 +// Remark9 = 0xf9 +// Remark10 = 0xfa +// Remark11 = 0xfb +// Remark12 = 0xfc +// Remark13 = 0xfd +// Remark14 = 0xfe +// Remark15 = 0xff +// This function uses `Neo.Attribute.GetUsage` syscall internally. func GetUsage(attr Attribute) byte { return 0x00 } -// GetData returns the data of the given attribute. +// GetData returns the data of the given attribute, exact interpretation of this +// data depends on attribute's Usage type. It uses `Neo.Attribute.GetData` +// syscall internally. func GetData(attr Attribute) []byte { return nil } From 9718891d793d75809af51cd0ccb8e0deeecc1903 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 21:32:07 +0300 Subject: [PATCH 07/35] compiler: add support for attribute syscalls --- pkg/compiler/syscall.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 4ac9b1c37..40365cc99 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -7,6 +7,10 @@ var syscalls = map[string]map[string]string{ "GetVotes": "Neo.Account.GetVotes", "IsStandard": "Neo.Account.IsStandard", }, + "attribute": { + "GetUsage": "Neo.Attribute.GetUsage", + "GetData": "Neo.Attribute.GetData", + }, "crypto": { "ECDsaVerify": "Neo.Crypto.ECDsaVerify", }, From aaf65729bf0d82fc251f2d1934e4f2472e2be645 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 21:59:20 +0300 Subject: [PATCH 08/35] interop/block: update documentation, fix GetTransaction GetTransaction never worked with hash, it works with indexes. --- pkg/interop/block/block.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/pkg/interop/block/block.go b/pkg/interop/block/block.go index e7171614a..98cd3a50d 100644 --- a/pkg/interop/block/block.go +++ b/pkg/interop/block/block.go @@ -1,25 +1,30 @@ +/* +Package block provides getters for Neo Block structure. +*/ package block import "github.com/nspcc-dev/neo-go/pkg/interop/transaction" -// Package block provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Block stubs a NEO block type. +// Block represents a NEO block, it's an opaque data structure that you can get +// data from only using functions from this package. It's similar in function to +// the Block class in the Neo .net framework. To use it you need to get it via +// blockchain.GetBlock function call. type Block struct{} -// GetTransactionCount returns the number of recorded transactions in the given block. +// GetTransactionCount returns the number of recorded transactions in the given +// block. It uses `Neo.Block.GetTransactionCount` syscall internally. func GetTransactionCount(b Block) int { return 0 } // GetTransactions returns a slice of transactions recorded in the given block. +// It uses `Neo.Block.GetTransactions` syscall internally. func GetTransactions(b Block) []transaction.Transaction { return []transaction.Transaction{} } -// GetTransaction returns a transaction from the given a block hash of the -// transaction. -func GetTransaction(b Block, hash []byte) transaction.Transaction { +// GetTransaction returns transaction from the given block by its index. It +// uses `Neo.Block.GetTransaction` syscall internally. +func GetTransaction(b Block, index int) transaction.Transaction { return transaction.Transaction{} } From afe23ce239a6c4fd9a97cf60c5c735bf2ee0cea2 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 23:30:16 +0300 Subject: [PATCH 09/35] interop/blockchain: update documentation --- pkg/interop/blockchain/blockchain.go | 45 +++++++++++++++++++++------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go index 18ae7216b..120b8cec6 100644 --- a/pkg/interop/blockchain/blockchain.go +++ b/pkg/interop/blockchain/blockchain.go @@ -1,3 +1,6 @@ +/* +Package blockchain provides functions to access various blockchain data. +*/ package blockchain import ( @@ -9,45 +12,65 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/transaction" ) -// Package blockchain provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// GetHeight returns the height of te block recorded in the current execution scope. +// GetHeight returns current block height (index of the last accepted block). +// Note that when transaction is being run as a part of new block this block is +// considered as not yet accepted (persisted) and thus you'll get an index of +// the previous (already accepted) block. This function uses +// `Neo.Blockchain.GetHeight` syscall. func GetHeight() int { return 0 } -// GetHeader returns the header found by the given hash or index. +// GetHeader returns header found by the given hash (256 bit hash in BE format +// represented as a slice of 32 bytes) or index (integer). Refer to the `header` +// package for possible uses of returned structure. This function uses +// `Neo.Blockchain.GetHeader` syscall. func GetHeader(heightOrHash interface{}) header.Header { return header.Header{} } -// GetBlock returns the block found by the given hash or index. +// GetBlock returns block found by the given hash or index (with the same +// encoding as for GetHeader). Refer to the `block` package for possible uses +// of returned structure. This function uses `Neo.Blockchain.GetBlock` syscall. func GetBlock(heightOrHash interface{}) block.Block { return block.Block{} } -// GetTransaction returns the transaction found by the given hash. +// GetTransaction returns transaction found by the given (256 bit in BE format +// represented as a slice of 32 bytes). Refer to the `transaction` package for +// possible uses of returned structure. This function uses +// `Neo.Blockchain.GetTransaction` syscall. func GetTransaction(hash []byte) transaction.Transaction { return transaction.Transaction{} } -// GetContract returns the contract found by the given script hash. +// GetContract returns contract found by the given script hash (160 bit in BE +// format represented as a slice of 20 bytes). Refer to the `contract` package +// for details on how to use the returned structure. This function uses +// `Neo.Blockchain.GetContract` syscall. func GetContract(scriptHash []byte) contract.Contract { return contract.Contract{} } -// GetAccount returns the account found by the given script hash. +// GetAccount returns account found by the given script hash (160 bit in BE +// format represented as a slice of 20 bytes). Refer to the `account` package +// for details on how to use the returned structure. This function uses +// `Neo.Blockchain.GetAccount` syscall. func GetAccount(scriptHash []byte) account.Account { return account.Account{} } -// GetValidators returns a slice of validator addresses. +// GetValidators returns a slice of current validators public keys represented +// as a compressed serialized byte slice (33 bytes long). This function uses +// `Neo.Blockchain.GetValidators` syscall. func GetValidators() [][]byte { return nil } -// GetAsset returns the asset found by the given asset id. +// GetAsset returns asset found by the given asset ID (256 bit in BE format +// represented as a slice of 32 bytes). Refer to the `asset` package for +// possible uses of returned structure. This function uses +// `Neo.Blockchain.GetAsset` syscall. func GetAsset(assetID []byte) asset.Asset { return asset.Asset{} } From 14409addd9897b4bf659301fcbc46931c539e2ad Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 23:35:56 +0300 Subject: [PATCH 10/35] compiler/interop: expose GetTransactionHeight And sort syscall names as they change the indentation anyway. --- pkg/compiler/syscall.go | 17 +++++++++-------- pkg/interop/blockchain/blockchain.go | 7 +++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 40365cc99..8eef83436 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -31,14 +31,15 @@ var syscalls = map[string]map[string]string{ "Deserialize": "Neo.Runtime.Deserialize", }, "blockchain": { - "GetHeight": "Neo.Blockchain.GetHeight", - "GetHeader": "Neo.Blockchain.GetHeader", - "GetBlock": "Neo.Blockchain.GetBlock", - "GetTransaction": "Neo.Blockchain.GetTransaction", - "GetContract": "Neo.Blockchain.GetContract", - "GetAccount": "Neo.Blockchain.GetAccount", - "GetValidators": "Neo.Blockchain.GetValidators", - "GetAsset": "Neo.Blockchain.GetAsset", + "GetAccount": "Neo.Blockchain.GetAccount", + "GetAsset": "Neo.Blockchain.GetAsset", + "GetBlock": "Neo.Blockchain.GetBlock", + "GetContract": "Neo.Blockchain.GetContract", + "GetHeader": "Neo.Blockchain.GetHeader", + "GetHeight": "Neo.Blockchain.GetHeight", + "GetTransaction": "Neo.Blockchain.GetTransaction", + "GetTransactionHeight": "Neo.Blockchain.GetTransactionHeight", + "GetValidators": "Neo.Blockchain.GetValidators", }, "header": { "GetIndex": "Neo.Header.GetIndex", diff --git a/pkg/interop/blockchain/blockchain.go b/pkg/interop/blockchain/blockchain.go index 120b8cec6..c38e4dd7d 100644 --- a/pkg/interop/blockchain/blockchain.go +++ b/pkg/interop/blockchain/blockchain.go @@ -44,6 +44,13 @@ func GetTransaction(hash []byte) transaction.Transaction { return transaction.Transaction{} } +// GetTransactionHeight returns transaction's height (index of the block that +// includes it) by the given ID (256 bit in BE format represented as a slice of +// 32 bytes). This function uses `Neo.Blockchain.GetTransactionHeight` syscall. +func GetTransactionHeight(hash []byte) int { + return 0 +} + // GetContract returns contract found by the given script hash (160 bit in BE // format represented as a slice of 20 bytes). Refer to the `contract` package // for details on how to use the returned structure. This function uses From 4284eaf4ab17c815e7220c0290a82a682a5c6ee2 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 23:56:52 +0300 Subject: [PATCH 11/35] gitignore: don't ignore blockchain(s) directories It interferes with interop/blockchain and it's not a default directory for chain DBs. --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index cb8e5a620..6fe160927 100644 --- a/.gitignore +++ b/.gitignore @@ -31,8 +31,6 @@ TAGS # leveldb chains/ chain/ -blockchain/ -blockchains/ # patch *.orig From 823195384208ed14dc4211603f71e24fb3354f9d Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 17 May 2020 23:58:23 +0300 Subject: [PATCH 12/35] core: fix Neo.Contract.GetStorageContext security check This syscall should only work for contracts created by current transaction and that is what is supposed to be checked here. Do so by looking at the differences between ic.dao and original lower DAO. --- pkg/core/interop/context.go | 2 ++ pkg/core/interop_system.go | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 59b2337b0..61e56ad59 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -27,6 +27,7 @@ type Context struct { Block *block.Block Tx *transaction.Transaction DAO *dao.Cached + LowerDAO dao.DAO Notifications []state.NotificationEvent Log *zap.Logger } @@ -42,6 +43,7 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, n Block: block, Tx: tx, DAO: dao, + LowerDAO: d, Notifications: nes, Log: log, } diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 4f6bfea4b..b6dc95aa6 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -537,8 +537,12 @@ func contractGetStorageContext(ic *interop.Context, v *vm.VM) error { if !ok { return fmt.Errorf("%T is not a contract state", cs) } - contractState, err := ic.DAO.GetContractState(cs.ScriptHash()) - if contractState == nil || err != nil { + _, err := ic.DAO.GetContractState(cs.ScriptHash()) + if err != nil { + return fmt.Errorf("non-existent contract") + } + _, err = ic.LowerDAO.GetContractState(cs.ScriptHash()) + if err == nil { return fmt.Errorf("contract was not created in this transaction") } stc := &StorageContext{ From 1e83a5e31804c25496b6fa231fba3bcac987d425 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 10:42:44 +0300 Subject: [PATCH 13/35] interop/contract: update documentation, fix some interfaces Some functions were just not correct in their interfaces. --- pkg/interop/contract/contract.go | 68 +++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/pkg/interop/contract/contract.go b/pkg/interop/contract/contract.go index f1d6af376..d220b85e2 100644 --- a/pkg/interop/contract/contract.go +++ b/pkg/interop/contract/contract.go @@ -1,55 +1,85 @@ +/* +Package contract provides functions to work with contracts. +*/ package contract import "github.com/nspcc-dev/neo-go/pkg/interop/storage" -// Package contract provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Contract stubs a NEO contract type. +// Contract represents a Neo contract and is used in interop functions. It's +// an opaque data structure that you can manipulate with using functions from +// this package. It's similar in function to the Contract class in the Neo .net +// framework. type Contract struct{} -// GetScript returns the script of the given contract. +// GetScript returns the script of the given contract. It uses +// `Neo.Contract.GetScript` syscall. func GetScript(c Contract) []byte { return nil } -// IsPayable returns whether the given contract is payable. +// IsPayable returns whether the given contract is payable (able to accept +// asset transfers to its address). It uses `Neo.Contract.IsPayable` syscall. func IsPayable(c Contract) bool { return false } -// GetStorageContext returns the storage context for the given contract. +// GetStorageContext returns storage context for the given contract. It only +// works for contracts created in this transaction (so you can't take a storage +// context for arbitrary contract). Refer to the `storage` package on how to +// use this context. This function uses `Neo.Contract.GetStorageContext` syscall. func GetStorageContext(c Contract) storage.Context { return storage.Context{} } -// Create creates a new contract. -// @FIXME What is the type of the returnType here? +// Create creates a new contract using a set of input parameters: +// script contract's bytecode (limited in length by 1M) +// params contract's input parameter types, one byte per parameter, see +// ParamType in the `smartcontract` package for value +// definitions. Maximum number of parameters: 252. +// returnType return value type, also a ParamType constant +// properties bit field with contract's permissions (storage, dynamic +// invoke, payable), see PropertyState in the `smartcontract` +// package +// name human-readable contract name (no longer than 252 bytes) +// version human-readable contract version (no longer than 252 bytes) +// author contract's author (no longer than 252 bytes) +// email contract's author/support e-mail (no longer than 252 bytes) +// description human-readable contract description (no longer than 64K bytes) +// It returns this new created Contract when successful (and fails transaction +// if not). It uses `Neo.Contract.Create` syscall. func Create( script []byte, - params []interface{}, + params []byte, returnType byte, - properties interface{}, + properties byte, name, version, author, email, - description string) { + description string) Contract { + return Contract{} } -// Migrate migrates a new contract. -// @FIXME What is the type of the returnType here? +// Migrate migrates calling contract (that is the one that calls Migrate) to +// the new contract. Its parameters have exactly the same semantics as for +// Create. The old contract will be deleted by this call, if it has any storage +// associated it will be migrated to the new contract. New contract is returned. +// This function uses `Neo.Contract.Migrate` syscall. func Migrate( script []byte, - params []interface{}, + params []byte, returnType byte, - properties interface{}, + properties byte, name, version, author, email, - description string) { + description string) Contract { + return Contract{} } -// Destroy deletes a contract that is registered on the blockchain. -func Destroy(c Contract) {} +// Destroy deletes calling contract (the one that calls Destroy) from the +// blockchain, so it's only possible to do that from the contract itself and +// not by any outside code. When contract is deleted all associated storage +// items are deleted too. This function uses `Neo.Contract.Destroy` syscall. +func Destroy() {} From 71f29c1f9f8366bffb1ec15fc2cf32de9a24dfb1 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 10:58:27 +0300 Subject: [PATCH 14/35] interop/crypto: update documentation --- pkg/interop/crypto/crypto.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/interop/crypto/crypto.go b/pkg/interop/crypto/crypto.go index dfcab6a5d..fa2d394f6 100644 --- a/pkg/interop/crypto/crypto.go +++ b/pkg/interop/crypto/crypto.go @@ -1,14 +1,15 @@ +/* +Package crypto provides an interface to cryptographic syscalls. +*/ package crypto -// Package crypto provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// SHA256 computes the sha256 hash of b. +// SHA256 computes SHA256 hash of b. It uses `Neo.Crypto.SHA256` syscall. func SHA256(b []byte) []byte { return nil } -// ECDsaVerify checks that sig is msg's signature with pub. +// ECDsaVerify checks that sig is correct msg's signature for a given pub +// (serialized public key). It uses `Neo.Crypto.ECDsaVerify` syscall. func ECDsaVerify(msg []byte, pub []byte, sig []byte) bool { return false } From a44bdea8902f36b06e96717ac368869d5a94fdb9 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 12:01:00 +0300 Subject: [PATCH 15/35] interop/engine: update documentation --- pkg/interop/engine/engine.go | 44 +++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/pkg/interop/engine/engine.go b/pkg/interop/engine/engine.go index 06a54e3fc..c2ebda9dd 100644 --- a/pkg/interop/engine/engine.go +++ b/pkg/interop/engine/engine.go @@ -1,34 +1,56 @@ +/* +Package engine provides access to VM execution metadata and allows to make contract calls. +It's roughly similar in function to ExecutionEngine class in the Neo .net +framework. +*/ package engine import "github.com/nspcc-dev/neo-go/pkg/interop/transaction" -// Package engine provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// GetScriptContainer returns the transaction that is in the execution context. +// GetScriptContainer returns the transaction that initially triggered current +// execution context. It never changes in a single execution, no matter how deep +// this execution goes. See `transaction` package for details on how to use the +// returned value. This function uses `System.ExecutionEngine.GetScriptContainer` +// syscall. func GetScriptContainer() transaction.Transaction { return transaction.Transaction{} } -// GetExecutingScriptHash returns the script hash of the contract that is -// currently being executed. +// GetExecutingScriptHash returns script hash (160 bit in BE form represented +// as 20-byte slice) of the contract that is currently being executed. Any +// AppCall can change the value returned by this function if it calls a +// different contract. This function uses +// `System.ExecutionEngine.GetExecutingScriptHash` syscall. func GetExecutingScriptHash() []byte { return nil } -// GetCallingScriptHash returns the script hash of the contract that started -// the execution of the current script. +// GetCallingScriptHash returns script hash (160 bit in BE form represented +// as 20-byte slice) of the contract that started the execution of the currently +// running context (caller of current contract or function), so it's one level +// above the GetExecutingScriptHash in the call stack. It uses +// `System.ExecutionEngine.GetCallingScriptHash` syscall. func GetCallingScriptHash() []byte { return nil } -// GetEntryScriptHash returns the script hash of the contract that started the -// execution from the start. +// GetEntryScriptHash returns script hash (160 bit in BE form represented +// as 20-byte slice) of the contract that initially started current execution +// (this is a script that is contained in a transaction returned by +// GetScriptContainer) execution from the start. This function uses +// `System.ExecutionEngine.GetEntryScriptHash` syscall. func GetEntryScriptHash() []byte { return nil } -// AppCall executes script with specified hash using provided arguments. +// AppCall executes previously deployed blockchain contract with specified hash +// (160 bit in BE form represented as 20-byte slice) using provided arguments. +// It returns whatever this contract returns. Even though this function accepts +// a slice for scriptHash you can only use it for contracts known at +// compile time, because there is a significant difference between static and +// dynamic calls in Neo (contracts should have a special property declared +// and paid for to be able to use dynamic calls). This function uses +// `System.Contract.Call` syscall. func AppCall(scriptHash []byte, args ...interface{}) interface{} { return nil } From a0e9673c3e6a521ecd77585add4e413f10da99d2 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 13:19:01 +0300 Subject: [PATCH 16/35] interop/enumerator: update doc, fix Next return value --- pkg/interop/enumerator/enumerator.go | 36 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/pkg/interop/enumerator/enumerator.go b/pkg/interop/enumerator/enumerator.go index 0d5531757..a47bff76f 100644 --- a/pkg/interop/enumerator/enumerator.go +++ b/pkg/interop/enumerator/enumerator.go @@ -1,29 +1,41 @@ +/* +Package enumerator provides functions to work with enumerators. +*/ package enumerator -// Package enumerator provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// TODO: Check enumerator use cases and add them to the examples folder. - -// Enumerator stubs a NEO enumerator type. +// Enumerator represents NEO enumerator type, it's an opaque data structure +// that can be used with functions from this package. It's similar to more +// widely used Iterator (see `iterator` package), but ranging over arrays +// or structures that have values with no explicit keys. type Enumerator struct{} -// Create creates a new enumerator from the given items. +// Create creates a new enumerator from the given items (slice or structure). +// New enumerator points at index -1 of its items, so the user of it has to +// advance it first with Next. This function uses `Neo.Enumerator.Create` +// syscall. func Create(items []interface{}) Enumerator { return Enumerator{} } -// Next returns the next item in the iteration. -func Next(e Enumerator) interface{} { - return nil +// Next moves position of the given enumerator by one and returns a bool that +// tells whether there is a new value present in this new position. If it is, +// you can use Value to get it, if not then there are no more values in this +// enumerator. This function uses `Neo.Enumerator.Next` syscall. +func Next(e Enumerator) bool { + return true } -// Value returns the enumerator value. +// Value returns current enumerator's item value, it's only valid to call it +// after Next returning true. This function uses `Neo.Enumerator.Value` syscall. func Value(e Enumerator) interface{} { return nil } -// Concat concatenates the 2 given enumerators. +// Concat concatenates two given enumerators returning one that will range on +// a first and then continue with b. Enumerator positions are not reset for a +// and b, so if any of them was already advanced by Next the resulting +// Enumerator will point at this new position and never go back to previous +// values. This function uses `Neo.Enumerator.Concat` syscall. func Concat(a, b Enumerator) Enumerator { return Enumerator{} } From 4e21e43e7227f50bcb89682a2d494c7df01c1915 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 13:20:49 +0300 Subject: [PATCH 17/35] compiler: add support for enumerator interop package --- pkg/compiler/syscall.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 8eef83436..8f28a4d21 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -14,6 +14,12 @@ var syscalls = map[string]map[string]string{ "crypto": { "ECDsaVerify": "Neo.Crypto.ECDsaVerify", }, + "enumerator": { + "Concat": "Neo.Enumerator.Concat", + "Create": "Neo.Enumerator.Create", + "Next": "Neo.Enumerator.Next", + "Value": "Neo.Enumerator.Value", + }, "storage": { "GetContext": "Neo.Storage.GetContext", "Put": "Neo.Storage.Put", From 80b39649aa64a90a9af9a42f8d102ffc19a48cdb Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 14:23:55 +0300 Subject: [PATCH 18/35] interop/header: update documentation --- pkg/interop/header/header.go | 38 ++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/pkg/interop/header/header.go b/pkg/interop/header/header.go index 44fc2d9e6..37abe9676 100644 --- a/pkg/interop/header/header.go +++ b/pkg/interop/header/header.go @@ -1,47 +1,61 @@ +/* +Package header contains functions working with block headers. +*/ package header -// Package header provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Header stubs a NEO block header type. +// Header represents Neo block header type, it's an opaque data structure that +// can be used by functions from this package. You can create it with +// blockchain.GetHeader. In its function it's similar to the Header class +// of the Neo .net framework. type Header struct{} -// GetIndex returns the index of the given header. +// GetIndex returns the index (height) of the given header. It uses +// `Neo.Header.GetIndex` syscall. func GetIndex(h Header) int { return 0 } -// GetHash returns the hash of the given header. +// GetHash returns the hash (256-bit BE value packed into 32 byte slice) of the +// given header (which also is a hash of the block). It uses `Neo.Header.GetHash` +// syscall. func GetHash(h Header) []byte { return nil } -// GetPrevHash returns the previous hash of the given header. +// GetPrevHash returns the hash (256-bit BE value packed into 32 byte slice) of +// the previous block stored in the given header. It uses `Neo.Header.GetPrevHash` +// syscall. func GetPrevHash(h Header) []byte { return nil } -// GetTimestamp returns the timestamp of the given header. +// GetTimestamp returns the timestamp of the given header. It uses +// `Neo.Header.GetTimestamp` syscall. func GetTimestamp(h Header) int { return 0 } -// GetVersion returns the version of the given header. +// GetVersion returns the version of the given header. It uses +// `Neo.Header.GetVersion` syscall. func GetVersion(h Header) int { return 0 } -// GetMerkleRoot returns the merkle root of the given header. +// GetMerkleRoot returns the Merkle root (256-bit BE value packed into 32 byte +// slice) of the given header. It uses `Neo.Header.GetMerkleRoot` syscall. func GetMerkleRoot(h Header) []byte { return nil } -// GetConsensusData returns the consensus data of the given header. +// GetConsensusData returns the consensus data (nonce) of the given header. +// It uses `Neo.Header.GetConsensusData` syscall. func GetConsensusData(h Header) int { return 0 } -// GetNextConsensus returns the next consensus of the given header. +// GetNextConsensus returns the next consensus field (verification script hash, +// 160-bit BE value packed into 20 byte slice) of the given header. It uses +// `Neo.Header.GetNextConsensus` syscall. func GetNextConsensus(h Header) []byte { return nil } From 2bf7329a1518993f4a54227d6b784010e9b65e2c Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 14:37:38 +0300 Subject: [PATCH 19/35] interop/input|output: update documentation --- pkg/interop/input/input.go | 17 +++++++++++------ pkg/interop/output/output.go | 20 +++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/pkg/interop/input/input.go b/pkg/interop/input/input.go index 047637226..e577f2815 100644 --- a/pkg/interop/input/input.go +++ b/pkg/interop/input/input.go @@ -1,17 +1,22 @@ +/* +Package input provides functions dealing with transaction inputs. +*/ package input -// Package input provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Input stubs the input of a NEO transaction. +// Input is an opaque data structure that can only be created by +// transaction.GetInputs and it represents transaction's input. It's similar +// to Neo .net framework's TransactionInput. type Input struct{} -// GetHash returns the hash of the given input. +// GetHash returns the hash stored in the given input (which also is a +// transaction ID represented as 32 byte slice containing 256 bit BE value). +// It uses `Neo.Input.GetHash` syscall. func GetHash(in Input) []byte { return nil } -// GetIndex returns the index of the given input. +// GetIndex returns the index stored in the given input (which is a +// transaction's output number). It uses `Neo.Input.GetIndex` syscall. func GetIndex(in Input) int { return 0 } diff --git a/pkg/interop/output/output.go b/pkg/interop/output/output.go index 6f40ac544..01ad0c828 100644 --- a/pkg/interop/output/output.go +++ b/pkg/interop/output/output.go @@ -1,22 +1,28 @@ +/* +Package output provides functions dealing with transaction outputs. +*/ package output -// Package output provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Output stubs the output of a NEO transaction. +// Output is an opaque data structure that can only be created by +// transaction.GetOutputs and it represents transaction's output. It's similar +// to Neo .net framework's TransactionOutput. type Output struct{} -// GetAssetID returns the asset id of the given output. +// GetAssetID returns the asset ID (256 bit BE value in a 32 byte slice) of the +// given output. It uses `Neo.Output.GetAssetId` syscall. func GetAssetID(out Output) []byte { return nil } -// GetValue returns the value of the given output. +// GetValue returns the value (asset quantity) of the given output. It uses +// `Neo.Output.GetValue` syscall. func GetValue(out Output) int { return 0 } -// GetScriptHash returns the script hash of the given output. +// GetScriptHash returns the script hash (receiver's address represented as +// 20 byte slice containing 160 bit BE value) of the given output. It uses +// `Neo.Output.GetScriptHash` syscall. func GetScriptHash(out Output) []byte { return nil } From 01b5299355731d1b6857a7d246eb87d9d8124f71 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 15:30:44 +0300 Subject: [PATCH 20/35] interop/iterator: documentation update --- pkg/interop/iterator/iterator.go | 45 +++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/pkg/interop/iterator/iterator.go b/pkg/interop/iterator/iterator.go index 84e85310e..b4bd281da 100644 --- a/pkg/interop/iterator/iterator.go +++ b/pkg/interop/iterator/iterator.go @@ -1,39 +1,54 @@ +/* +Package iterator provides functions to work with Neo iterators. +*/ package iterator -// Package iterator provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. +import "github.com/nspcc-dev/neo-go/pkg/interop/enumerator" -// Iterator stubs a NEO iterator object type. +// Iterator represents a Neo iterator, it's an opaque data structure that can +// be properly created by Create or storage.Find. Unlike enumerators, iterators +// range over key-value pairs, so it's convenient to use them for maps. This +// structure is similar in function to Neo .net framework's Iterator. type Iterator struct{} -// Create creates an iterator from the given items. +// Create creates an iterator from the given items (array, struct or map). A new +// iterator is set to point at element -1, so to access its first element you +// need to call Next first. This function uses `Neo.Iterator.Create` syscall. func Create(items []interface{}) Iterator { return Iterator{} } -// Key returns the iterator key. -// TODO: Better description for this. +// Key returns iterator's key at current position. It's only valid to call after +// successful Next call. This function uses `Neo.Iterator.Key` syscall. func Key(it Iterator) interface{} { return nil } -// Keys returns the iterator keys. -func Keys(it Iterator) []interface{} { - return nil +// Keys returns Enumerator ranging over keys or the given Iterator. Note that +// this Enumerator is actually directly tied to the underlying Iterator, so that +// advancing it with Next will actually advance the Iterator too. This function +// uses `Neo.Iterator.Keys` syscall. +func Keys(it Iterator) enumerator.Enumerator { + return enumerator.Enumerator{} } -// Next advances the iterator, return true if it is was successful -// and false otherwise. +// Next advances the iterator returning true if it is was successful (and you +// can use Key or Value) and false otherwise (and there are no more elements in +// this Iterator). This function uses `Neo.Iterator.Next` syscall. func Next(it Iterator) bool { return true } -// Value returns the current iterator value. +// Value returns iterator's current value. It's only valid to call after +// successful Next call. This function uses `Neo.Iterator.Value` syscall. func Value(it Iterator) interface{} { return nil } -// Values returns the iterator values. -func Values(it Iterator) []interface{} { - return nil +// Values returns Enumerator ranging over values or the given Iterator. Note that +// this Enumerator is actually directly tied to the underlying Iterator, so that +// advancing it with Next will actually advance the Iterator too. This function +// uses `Neo.Iterator.Values` syscall. +func Values(it Iterator) enumerator.Enumerator { + return enumerator.Enumerator{} } From 6d2b2d7263293d1fffda41d47b00f1ad196fb3a3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 15:38:11 +0300 Subject: [PATCH 21/35] compiler/iterator: add missing iterator.Concat function We have a syscall for it, so it should be exposed. --- pkg/compiler/syscall.go | 1 + pkg/interop/iterator/iterator.go | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 8f28a4d21..cf63188cc 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -108,6 +108,7 @@ var syscalls = map[string]map[string]string{ "GetExecutingScriptHash": "System.ExecutionEngine.GetExecutingScriptHash", }, "iterator": { + "Concat": "Neo.Iterator.Concat", "Create": "Neo.Iterator.Create", "Key": "Neo.Iterator.Key", "Keys": "Neo.Iterator.Keys", diff --git a/pkg/interop/iterator/iterator.go b/pkg/interop/iterator/iterator.go index b4bd281da..ec407ee0a 100644 --- a/pkg/interop/iterator/iterator.go +++ b/pkg/interop/iterator/iterator.go @@ -18,6 +18,17 @@ func Create(items []interface{}) Iterator { return Iterator{} } +// Concat concatenates two given iterators returning one that will range on +// a first and then continue with b. Iterator positions are not reset for a +// and b, so if any of them was already advanced by Next the resulting +// Iterator will point at this new position and never go back to previous +// key-value pairs. Concatenated iterators also remain completely independent +// in results they return, so if both contain the same key you'll receive this +// key twice when iterating. This function uses `Neo.Iterator.Concat` syscall. +func Concat(a, b Iterator) Iterator { + return Iterator{} +} + // Key returns iterator's key at current position. It's only valid to call after // successful Next call. This function uses `Neo.Iterator.Key` syscall. func Key(it Iterator) interface{} { From ddb94976499cbdf3771ccc525e52a72aeb52a2d0 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 17:11:27 +0300 Subject: [PATCH 22/35] interop/runtime: update documentation, fix Notify Notify doesn't return anything! --- pkg/interop/runtime/runtime.go | 56 +++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/pkg/interop/runtime/runtime.go b/pkg/interop/runtime/runtime.go index fd07d7bd6..9b7e837d3 100644 --- a/pkg/interop/runtime/runtime.go +++ b/pkg/interop/runtime/runtime.go @@ -1,48 +1,70 @@ +/* +Package runtime provides various service functions related to execution environment. +It has similar function to Runtime class in .net framwork for Neo. +*/ package runtime -// Package runtime provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// CheckWitness verifies if the given hash is the invoker of the contract. -func CheckWitness(hash []byte) bool { +// CheckWitness verifies if the given script hash (160-bit BE value in a 20 byte +// slice) or key (compressed serialized 33-byte form) is one of the signers of +// this invocation. It uses `Neo.Runtime.CheckWitness` syscall. +func CheckWitness(hashOrKey []byte) bool { return true } -// Log instucts the VM to log the given message. +// Log instructs VM to log the given message. It's mostly used for debugging +// purposes as these messages are not saved anywhere normally and usually are +// only visible in the VM logs. This function uses `Neo.Runtime.Log` syscall. func Log(message string) {} -// Notify an event to the VM. -func Notify(arg ...interface{}) int { - return 0 -} +// Notify sends a notification (collecting all arguments in an array) to the +// executing environment. Unlike Log it can accept any data and resulting +// notification is saved in application log. It's intended to be used as a +// part of contract's API to external systems, these events can be monitored +// from outside and act upon accordingly. This function uses +// `Neo.Runtime.Notify` syscall. +func Notify(arg ...interface{}) {} -// GetTime returns the timestamp of the most recent block. +// GetTime returns the timestamp of the most recent block. Note that when running +// script in test mode this would be the last accepted (persisted) block in the +// chain, but when running as a part of the new block the time returned is the +// time of this (currently being processed) block. This function uses +// `Neo.Runtime.GetTime` syscall. func GetTime() int { return 0 } -// GetTrigger returns the smart contract invoke trigger which can be either -// verification or application. +// GetTrigger returns the smart contract invocation trigger which can be either +// verification or application. It can be used to differentiate running contract +// as a part of verification process from running it as a regular application. +// Some interop functions (especially ones that change the state in any way) are +// not available when running with verification trigger. This function uses +// `Neo.Runtime.GetTrigger` syscall. func GetTrigger() byte { return 0x00 } -// Application returns the Application trigger type +// Application returns the Application trigger type value to compare with +// GetTrigger return value. func Application() byte { return 0x10 } -// Verification returns the Verification trigger type +// Verification returns the Verification trigger type value to compare with +// GetTrigger return value. func Verification() byte { return 0x00 } -// Serialize serializes and item into a bytearray. +// Serialize serializes any given item into a byte slice. It works for all +// regular VM types (not ones from interop package) and allows to save them in +// storage or pass into Notify and then Deserialize them on the next run or in +// the external event receiver. It uses `Neo.Runtime.Serialize` syscall. func Serialize(item interface{}) []byte { return nil } -// Deserialize an item from a bytearray. +// Deserialize unpacks previously serialized value from a byte slice, it's the +// opposite of Serialize. It uses `Neo.Runtime.Deserialize` syscall. func Deserialize(b []byte) interface{} { return nil } From bfd8fe59600cc36fa2245a0e56efff882c785d5e Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 18:06:12 +0300 Subject: [PATCH 23/35] interop/storage: update documentation --- pkg/interop/storage/storage.go | 35 +++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index be0f10bc0..aade7e1e3 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -1,24 +1,41 @@ +/* +Package storage provides functions to access and modify contract's storage. +Neo storage's model follows simple key-value DB pattern, this storage is a part +of blockchain state, so you can use it between various invocations of the same +contract. +*/ package storage import "github.com/nspcc-dev/neo-go/pkg/interop/iterator" -// Package storage provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Context represents the storage context. +// Context represents storage context that is mandatory for Put/Get/Delete +// operations. It's an opaque type that can only be created properly by +// GetContext. It's similar to Neo .net framework's StorageContext class. type Context struct{} -// GetContext returns the storage context. +// GetContext returns current contract's (that invokes this function) storage +// context. It uses `Neo.Storage.GetContext` syscall. func GetContext() Context { return Context{} } -// Put value at given key. +// Put saves given value with given key in the storage using given Context. +// Even though it accepts interface{} for both, you can only pass simple types +// there like string, []byte, int or bool (not structures or slices of more +// complex types). To put more complex types there serialize them first using +// runtime.Serialize. This function uses `Neo.Storage.Put` syscall. func Put(ctx Context, key interface{}, value interface{}) {} -// Get value matching given key. +// Get retrieves value stored for the given key using given Context. See Put +// documentation on possible key and value types. This function uses +// `Neo.Storage.Get` syscall. func Get(ctx Context, key interface{}) interface{} { return 0 } -// Delete key value pair from storage. +// Delete removes key-value pair from storage by the given key using given +// Context. See Put documentation on possible key types. This function uses +// `Neo.Storage.Delete` syscall. func Delete(ctx Context, key interface{}) {} -// Find returns an iterator.Iterator over the keys that matched the given key. +// Find returns an iterator.Iterator over key-value pairs in the given Context +// that match the given key (contain it as a prefix). See Put documentation on +// possible key types and iterator package documentation on how to use the +// returned value. This function uses `Neo.Storage.Find` syscall. func Find(ctx Context, key interface{}) iterator.Iterator { return iterator.Iterator{} } From 559e8a16da38d91b3a2790c80fe60b9ec8c784d0 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 18:13:45 +0300 Subject: [PATCH 24/35] compiler/storage: add read-only related interops --- pkg/compiler/syscall.go | 12 +++++++----- pkg/interop/storage/storage.go | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index cf63188cc..c906a569c 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -21,11 +21,13 @@ var syscalls = map[string]map[string]string{ "Value": "Neo.Enumerator.Value", }, "storage": { - "GetContext": "Neo.Storage.GetContext", - "Put": "Neo.Storage.Put", - "Get": "Neo.Storage.Get", - "Delete": "Neo.Storage.Delete", - "Find": "Neo.Storage.Find", + "ConvertContextToReadOnly": "Neo.StorageContext.AsReadOnly", + "Delete": "Neo.Storage.Delete", + "Find": "Neo.Storage.Find", + "Get": "Neo.Storage.Get", + "GetContext": "Neo.Storage.GetContext", + "GetReadOnlyContext": "Neo.Storage.GetReadOnlyContext", + "Put": "Neo.Storage.Put", }, "runtime": { "GetTrigger": "Neo.Runtime.GetTrigger", diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index aade7e1e3..a047eebb1 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -10,13 +10,26 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/iterator" // Context represents storage context that is mandatory for Put/Get/Delete // operations. It's an opaque type that can only be created properly by -// GetContext. It's similar to Neo .net framework's StorageContext class. +// GetContext, GetReadOnlyContext or ConvertContextToReadOnly. It's similar +// to Neo .net framework's StorageContext class. type Context struct{} +// ConvertContextToReadOnly returns new context from the given one, but with +// writing capability turned off, so that you could only invoke Get and Find +// using this new Context. If Context is already read-only this function is a +// no-op. It uses `Neo.StorageContext.AsReadOnly` syscall. +func ConvertContextToReadOnly(ctx Context) Context { return Context{} } + // GetContext returns current contract's (that invokes this function) storage // context. It uses `Neo.Storage.GetContext` syscall. func GetContext() Context { return Context{} } +// GetReadOnlyContext returns current contract's (that invokes this function) +// storage context in read-only mode, you can use this context for Get and Find +// functions, but using it for Put and Delete will fail. It uses +// `Neo.Storage.GetReadOnlyContext` syscall. +func GetReadOnlyContext() Context { return Context{} } + // Put saves given value with given key in the storage using given Context. // Even though it accepts interface{} for both, you can only pass simple types // there like string, []byte, int or bool (not structures or slices of more From 1cbd75ab9ed65d44a2606ed056bb468de5eb536d Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 18:22:07 +0300 Subject: [PATCH 25/35] compiler|transaction: fix transaction.GetScript build, add to interop There is no such syscall as Neo.Transaction.GetScript and GetScript should be available for contract's use. --- pkg/compiler/syscall.go | 2 +- pkg/interop/transaction/transaction.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index c906a569c..985a96509 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -72,7 +72,7 @@ var syscalls = map[string]map[string]string{ "GetOutputs": "Neo.Transaction.GetOutputs", "GetReferences": "Neo.Transaction.GetReferences", "GetUnspentCoins": "Neo.Transaction.GetUnspentCoins", - "GetScript": "Neo.Transaction.GetScript", + "GetScript": "Neo.InvocationTransaction.GetScript", }, "asset": { "Create": "Neo.Asset.Create", diff --git a/pkg/interop/transaction/transaction.go b/pkg/interop/transaction/transaction.go index 1d863f5c3..354fcf019 100644 --- a/pkg/interop/transaction/transaction.go +++ b/pkg/interop/transaction/transaction.go @@ -48,3 +48,10 @@ func GetInputs(t Transaction) []input.Input { func GetOutputs(t Transaction) []output.Output { return []output.Output{} } + +// GetScript returns the script stored in a given Invocation transaction. +// Calling it for any other Transaction type would lead to failure. It uses +// `Neo.InvocationTransaction.GetScript` syscall. +func GetScript(t Transaction) []byte { + return nil +} From e32d0e01afb4c89427c7ee0151f0723c4da998a4 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 18:33:55 +0300 Subject: [PATCH 26/35] compiler|transaction: remove transaction.GetUnspentCoins support It's useless. Even though there is Neo.Transaction.GetUnspentCoins syscall that can be used, its return type is an interop structure that's not accepted by any other syscall, so you can't really do anything with it. And there is no such interface for the .net Framework. --- docs/runtime.md | 6 ------ pkg/compiler/syscall.go | 15 +++++++-------- pkg/interop/transaction/transaction.go | 6 ------ 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/docs/runtime.md b/docs/runtime.md index e98b3af20..7f047db2f 100644 --- a/docs/runtime.md +++ b/docs/runtime.md @@ -504,12 +504,6 @@ GetReferences(t Transacfion) interface{} ``` Returns the references of the given transaction. -#### GetUnspentCoins -``` -GetUnspentCoins(t Transacfion) interface{} -``` -Returns the unspent coins of the given transaction. - #### GetOutputs ``` GetOutputs(t Transacfion) []output.Output diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 985a96509..16b6b26b8 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -65,14 +65,13 @@ var syscalls = map[string]map[string]string{ "GetTransaction": "Neo.Block.GetTransaction", }, "transaction": { - "GetHash": "Neo.Transaction.GetHash", - "GetType": "Neo.Transaction.GetType", - "GetAttributes": "Neo.Transaction.GetAttributes", - "GetInputs": "Neo.Transaction.GetInputs", - "GetOutputs": "Neo.Transaction.GetOutputs", - "GetReferences": "Neo.Transaction.GetReferences", - "GetUnspentCoins": "Neo.Transaction.GetUnspentCoins", - "GetScript": "Neo.InvocationTransaction.GetScript", + "GetAttributes": "Neo.Transaction.GetAttributes", + "GetHash": "Neo.Transaction.GetHash", + "GetInputs": "Neo.Transaction.GetInputs", + "GetOutputs": "Neo.Transaction.GetOutputs", + "GetReferences": "Neo.Transaction.GetReferences", + "GetScript": "Neo.InvocationTransaction.GetScript", + "GetType": "Neo.Transaction.GetType", }, "asset": { "Create": "Neo.Asset.Create", diff --git a/pkg/interop/transaction/transaction.go b/pkg/interop/transaction/transaction.go index 354fcf019..b412fe92f 100644 --- a/pkg/interop/transaction/transaction.go +++ b/pkg/interop/transaction/transaction.go @@ -33,12 +33,6 @@ func GetReferences(t Transaction) []interface{} { return []interface{}{} } -// GetUnspentCoins returns the unspent coins for the given transaction. -// FIXME: What is the correct return type for this? -func GetUnspentCoins(t Transaction) interface{} { - return 0 -} - // GetInputs returns the inputs of the given transaction. func GetInputs(t Transaction) []input.Input { return []input.Input{} From 85cc0639bb7915f97a6c6411b1819cc47fd9045e Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 18:52:30 +0300 Subject: [PATCH 27/35] interop/transaction: update documentation --- pkg/interop/transaction/transaction.go | 45 +++++++++++++++++++------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/pkg/interop/transaction/transaction.go b/pkg/interop/transaction/transaction.go index b412fe92f..0969da5b7 100644 --- a/pkg/interop/transaction/transaction.go +++ b/pkg/interop/transaction/transaction.go @@ -1,3 +1,6 @@ +/* +Package transaction provides functions to work with transactions. +*/ package transaction import ( @@ -6,39 +9,59 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/output" ) -// Package transaction provides function signatures that can be used inside -// smart contracts that are written in the neo-go framework. - -// Transaction stubs a NEO transaction type. +// Transaction represents a NEO transaction, it's an opaque data structure +// that can be used with functions from this package. It's similar to +// Transaction class in Neo .net framework. type Transaction struct{} -// GetHash returns the hash of the given transaction. +// GetHash returns the hash (256 bit BE value in a 32 byte slice) of the given +// transaction (which also is its ID). Is uses `Neo.Transaction.GetHash` syscall. func GetHash(t Transaction) []byte { return nil } -// GetType returns the type of the given transaction. +// GetType returns the type of the given transaction. Possible values: +// MinerTransaction = 0x00 +// IssueTransaction = 0x01 +// ClaimTransaction = 0x02 +// EnrollmentTransaction = 0x20 +// RegisterTransaction = 0x40 +// ContractTransaction = 0x80 +// StateType = 0x90 +// AgencyTransaction = 0xb0 +// PublishTransaction = 0xd0 +// InvocationTransaction = 0xd1 +// It uses `Neo.Transaction.GetType` syscall. func GetType(t Transaction) byte { return 0x00 } -// GetAttributes returns a slice of attributes for the given transaction. +// GetAttributes returns a slice of attributes for agiven transaction. Refer to +// attribute package on how to use them. This function uses +// `Neo.Transaction.GetAttributes` syscall. func GetAttributes(t Transaction) []attribute.Attribute { return []attribute.Attribute{} } -// GetReferences returns a slice of references for the given transaction. -// FIXME: What is the correct return type for this? +// GetReferences returns a slice of references for a given Transaction. Elements +// of this slice can be casted to any of input.Input or output.Output, depending +// on which information you're interested in (as reference technically contains +// both input and corresponding output), refer to input and output package on +// how to use them. This function uses `Neo.Transaction.GetReferences` syscall. func GetReferences(t Transaction) []interface{} { return []interface{}{} } -// GetInputs returns the inputs of the given transaction. +// GetInputs returns a slice of inputs of a given Transaction. Refer to input +// package on how to use them. This function uses `Neo.Transaction.GetInputs` +// syscall. func GetInputs(t Transaction) []input.Input { return []input.Input{} } -// GetOutputs returns the outputs of the given transaction. +// GetOutputs returns a slice of outputs of a given Transaction. Refer to output +// package on how to use them. This function uses `Neo.Transaction.GetOutputs` +// syscall. func GetOutputs(t Transaction) []output.Output { return []output.Output{} } From 87e7e157ae11322b1a283b72d4456bc1df58c3e3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 18:57:22 +0300 Subject: [PATCH 28/35] interop/util: extend documentation --- pkg/interop/util/util.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pkg/interop/util/util.go b/pkg/interop/util/util.go index 1f05d4941..1aba144cb 100644 --- a/pkg/interop/util/util.go +++ b/pkg/interop/util/util.go @@ -1,12 +1,19 @@ +/* +Package util contains some special useful functions that are provided by compiler and VM. +*/ package util -// FromAddress is an utility function that converts an NEO address to its hash. +// FromAddress is an utility function that converts a Neo address to its hash +// (160 bit BE value in a 20 byte slice). It can only be used for strings known +// at compilation time, because the convertion is actually being done by the +// compiler. func FromAddress(address string) []byte { return nil } -// Equals compares a with b and will return true whether a and b -// are equal. +// Equals compares a with b and will return true when a and b are equal. It's +// implemented as an EQUAL VM opcode, so the rules of comparison are those +// of EQUAL. func Equals(a, b interface{}) bool { return false } From 7e96f82a5bc9a8a17072ad3a5dbe78a8cad6e9d9 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 19:04:20 +0300 Subject: [PATCH 29/35] compiler/interop: add support for working with witnesses --- pkg/compiler/syscall.go | 4 ++++ pkg/interop/transaction/transaction.go | 8 ++++++++ pkg/interop/witness/witness.go | 14 ++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 pkg/interop/witness/witness.go diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index 16b6b26b8..d93a6cf1d 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -72,6 +72,7 @@ var syscalls = map[string]map[string]string{ "GetReferences": "Neo.Transaction.GetReferences", "GetScript": "Neo.InvocationTransaction.GetScript", "GetType": "Neo.Transaction.GetType", + "GetWitnesses": "Neo.Transaction.GetWitnesses", }, "asset": { "Create": "Neo.Asset.Create", @@ -117,4 +118,7 @@ var syscalls = map[string]map[string]string{ "Value": "Neo.Iterator.Value", "Values": "Neo.Iterator.Values", }, + "witness": { + "GetVerificationScript": "Neo.Witness.GetVerificationScript", + }, } diff --git a/pkg/interop/transaction/transaction.go b/pkg/interop/transaction/transaction.go index 0969da5b7..00d06c25e 100644 --- a/pkg/interop/transaction/transaction.go +++ b/pkg/interop/transaction/transaction.go @@ -7,6 +7,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/interop/attribute" "github.com/nspcc-dev/neo-go/pkg/interop/input" "github.com/nspcc-dev/neo-go/pkg/interop/output" + "github.com/nspcc-dev/neo-go/pkg/interop/witness" ) // Transaction represents a NEO transaction, it's an opaque data structure @@ -72,3 +73,10 @@ func GetOutputs(t Transaction) []output.Output { func GetScript(t Transaction) []byte { return nil } + +// GetWitnesses returns a slice of witnesses of a given Transaction. Refer to +// witness package on how to use them. This function uses +// `Neo.Transaction.GetWitnesses` syscall. +func GetWitnesses(t Transaction) []witness.Witness { + return []witness.Witness{} +} diff --git a/pkg/interop/witness/witness.go b/pkg/interop/witness/witness.go new file mode 100644 index 000000000..5bf742f83 --- /dev/null +++ b/pkg/interop/witness/witness.go @@ -0,0 +1,14 @@ +/* +Package witness provides functions dealing with transaction's witnesses. +*/ +package witness + +// Witness is an opaque data structure that can only be created by +// transaction.GetWitnesses and representing transaction's witness. +type Witness struct{} + +// GetVerificationScript returns verification script stored in the given +// witness. It uses `Neo.Witness.GetVerificationScript` syscall. +func GetVerificationScript(w Witness) []byte { + return nil +} From ab264deffe988f0df5f42a739f02f0053302f3e6 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 21:59:26 +0300 Subject: [PATCH 30/35] docs: drop runtime.md We have now way better godoc for interop functions, so this document makes little sense and it's not referenced anywhere, so it's safe to drop it. --- docs/runtime.md | 517 ------------------------------------------------ 1 file changed, 517 deletions(-) delete mode 100644 docs/runtime.md diff --git a/docs/runtime.md b/docs/runtime.md deleted file mode 100644 index 7f047db2f..000000000 --- a/docs/runtime.md +++ /dev/null @@ -1,517 +0,0 @@ -# Runtime -A brief overview of NEO smart contract API's that can be used in the neo-go framework. - -# Overview -1. [Account]() -2. [Asset]() -3. [Attribute]() -4. [Block]() -5. [Blockchain]() -6. [Contract]() -7. [Crypto]() -8. [Engine]() -9. [Enumerator]() -10. [Iterator]() -11. [Header]() -12. [Input]() -13. [Output]() -14. [Runtime]() -15. [Storage]() -16. [Transaction]() -17. [Util]() - -## Account -#### GetScriptHash -``` -GetScriptHash(a Account) []byte -``` -Returns the script hash of the given account. - -#### GetVotes -``` -GetVotes(a Account) [][]byte -``` -Returns the the votes (a slice of public keys) of the given account. - -#### GetBalance -``` -GetBalance(a Account, assetID []byte) int -``` -Returns the balance of the given asset id for the given account. - -## Asset -#### GetAssetID -``` -GetAssetID(a Asset) []byte -``` -Returns the id of the given asset. - -#### GetAmount -``` -GetAmount(a Asset) int -``` -Returns the amount of the given asset id. - -#### GetAvailable -``` -GetAvailable(a Asset) int -``` -Returns the available amount of the given asset. - -#### GetPrecision -``` -GetPrecision(a Asset) byte -``` -Returns the precision of the given Asset. - -#### GetOwner -``` -GetOwner(a Asset) []byte -``` -Returns the owner of the given asset. - -#### GetAdmin -``` -GetAdmin(a Asset) []byte -``` -Returns the admin of the given asset. - -#### GetIssuer -``` -GetIssuer(a Asset) []byte -``` -Returns the issuer of the given asset. - -#### Create -``` -Create(type byte, name string, amount int, precision byte, owner, admin, issuer []byte) -``` -Creates a new asset on the blockchain. - -#### Renew -``` -Renew(asset Asset, years int) -``` -Renews the given asset as long as the given years. - -## Attribute -#### GetUsage -``` -GetUsage(attr Attribute) []byte -``` -Returns the usage of the given attribute. - -#### GetData -``` -GetData(attr Attribute) []byte -``` -Returns the data of the given attribute. - -## Block -#### GetTransactionCount -``` -GetTransactionCount(b Block) int -``` -Returns the number of transactions that are recorded in the given block. - -#### GetTransactions -``` -GetTransactions(b Block) []transaction.Transaction -``` -Returns a slice of the transactions that are recorded in the given block. - -#### GetTransaction -``` -GetTransaction(b Block, hash []byte) transaction.Transaction -``` -Returns the transaction by the given hash that is recorded in the given block. - -## Blockchain -#### GetHeight -``` -GetHeight() int -``` -Returns the current height of the blockchain. - -#### GetHeader -``` -GetHeader(heightOrHash []interface{}) header.Header -``` -Return the header by the given hash or index. - -#### GetBlock -``` -GetBlock(heightOrHash interface{}) block.Block -``` -Returns the block by the given hash or index. - -#### GetTransaction -``` -GetTransaction(hash []byte) transaction.Transaction -``` -Returns a transaction by the given hash. - -#### GetContract -``` -GetContract(scriptHash []byte) contract.Contract -``` -Returns the contract found by the given script hash. - -#### GetAccount -``` -GetAccount(scriptHash []byte) account.Account -``` -Returns the account found by the given script hash. - -#### GetValiditors -``` -GetValidators() [][]byte -``` -Returns a list of validators public keys. - -#### GetAsset -``` -GetAsset(assetID []byte) asset.Asset -``` -Returns the asset found by the given asset id. - -## Contract -#### GetScript -``` -GetScript(c Contract) []byte -``` -Return the script of the given contract. - -#### IsPayable -``` -IsPayable(c Contract) bool -``` -Returns whether the given contract is payable. - -#### GetStorageContext -``` -GetStorageContext(c Contract) -``` -Returns the storage context of the given contract. - -#### Create -``` -Create( - script []byte, - params []interface{}, - returnType byte, - properties interface{}, - name, - version, - author, - email, - description string) -``` -Creates a new contract on the blockchain. - -#### Migrate -``` -Migrate( - script []byte, - params []interface{}, - returnType byte, - properties interface{}, - name, - version, - author, - email, - description string) -``` -Migrates a contract on the blockchain. - -#### Destroy -``` -Destroy(c Contract) -``` -Deletes the given contract from the blockchain. - -## Crypto -#### SHA1 -``` -SHA1(data []byte) []byte -``` -Computes the sha1 hash of the given bytes - -#### SHA256 -``` -SHA256(data []byte) []byte -``` -Computes the sha256 hash of the given bytes - -#### Hash256 -``` -Hash256(data []byte) []byte -``` -Computes the sha256^2 of the given data. - -#### Hash160 -``` -Hash160(data []byte) []byte) []byte -``` -Computes the ripemd160 over the sha256 hash of the given data. - -## Engine -#### GetScriptContainer -``` -GetScriptContainer() transaction.Transaction -``` -Returns the transaction that is in the context of the VM execution. - -#### GetExecutingScriptHash -``` -GetExecutingScriptHash() []byte -``` -Returns the script hash of the contract that is currently being executed. - -#### GetCallingScriptHash -``` -GetCallingScriptHash() []byte -``` -Returns the script hash of the contract that has started the execution of the current script. - -#### GetEntryScriptHash -``` -GetEntryScriptHash() []byte -``` -Returns the script hash of the contract that started the execution from the start. - -## Enumerator -#### Create -``` -Create(items []inteface{}) Enumerator -``` -Create a enumerator from the given items. - -#### Next -``` -Next(e Enumerator) interface{} -``` -Returns the next item from the given enumerator. - -#### Value -``` -Value(e Enumerator) interface{} -``` -Returns the enumerator value. - -## Iterator -#### Create -``` -Create(items []inteface{}) Iterator -``` -Creates an iterator from the given items. - -#### Key -``` -Key(it Iterator) interface{} -``` -Return the key from the given iterator. - -#### Keys -``` -Keys(it Iterator) []interface{} -``` -Returns the iterator's keys - -#### Values -``` -Values(it Iterator) []interface{} -``` -Returns the iterator's values - -## Header -#### GetIndex -``` -GetIndex(h Header) int -``` -Returns the height of the given header. - -#### GetHash -``` -GetHash(h Header) []byte -``` -Returns the hash of the given header. - -#### GetPrevHash -``` -GetPrevhash(h Header) []byte -``` -Returns the previous hash of the given header. - -#### GetTimestamp -``` -GetTimestamp(h Header) int -``` -Returns the timestamp of the given header. - -#### GetVersion -``` -GetVersion(h Header) int -``` -Returns the version of the given header. - -#### GetMerkleroot -``` -GetMerkleRoot(h Header) []byte -``` -Returns the merkle root of the given header. - -#### GetConsensusData -``` -GetConsensusData(h Header) int -``` -Returns the consensus data of the given header. - -#### GetNextConsensus -``` -GetNextConsensus(h Header) []byte -``` -Returns the next consensus of the given header. - -## Input -#### GetHash -``` -GetHash(in Input) []byte -``` -Returns the hash field of the given input. - -#### GetIndex -``` -GetIndex(in Input) int -``` -Returns the index field of the given input. - -## Output -#### GetAssetID -``` -GetAssetId(out Output) []byte -``` -Returns the asset id field of the given output. - -#### GetValue -``` -GetValue(out Output) int -``` -Returns the value field of the given output. - -#### GetScriptHash -``` -GetScriptHash(out Output) []byte -``` -Returns the script hash field of the given output. - -## Runtime -#### CheckWitness -``` -CheckWitness(hash []byte) bool -``` -Verifies if the given hash is the hash of the contract owner. - -#### Log -``` -Log(message string) -``` -Logs the given message. - -#### Notify -``` -Notify(args ...interface{}) int -``` -Notify any number of arguments to the VM. - -#### GetTime -``` -GetTime() int -``` -Returns the current time based on the highest block in the chain. - -#### GetTrigger -``` -GetTrigger() byte -``` -Returns the trigger type of the execution. - -#### Serialize -``` -Serialize(item interface{}) []byte -``` -Serialize the given stack item to a slice of bytes. - -#### Deserialize -``` -Deserialize(data []byte) interface{} -``` -Deserializes the given data to a stack item. - -## Storage -#### GetContext -``` -GetContext() Context -``` -Returns the current storage context. - -#### Put -``` -Put(ctx Context, key, value []interface{}) -``` -Stores the given value at the given key. - -#### Get -``` -Get(ctx Context, key interface{}) interface{} -``` -Returns the value found at the given key. - -#### Delete -``` -Delete(ctx Context, key interface{}) -``` -Delete's the given key from storage. - -#### Find -``` -Find(ctx Context, key interface{}) iterator.Iterator -``` -Find returns an iterator key-values that match the given key. - -## Transaction -#### GetHash -``` -GetHash(t Transacfion) []byte -``` -Returns the hash for the given transaction. - -#### GetType -``` -GetType(t Transacfion) byte -``` -Returns the type of the given transaction. - -#### GetAttributes -``` -GetAttributes(t Transacfion) []attribute.Attribute -``` -Returns the attributes of the given transaction. - -#### GetReferences -``` -GetReferences(t Transacfion) interface{} -``` -Returns the references of the given transaction. - -#### GetOutputs -``` -GetOutputs(t Transacfion) []output.Output -``` -Returns the outputs of the given transaction - -#### GetInputs -``` -GetInputs(t Transacfion) []input.Input -``` -Returns the inputs of the given transaction From 77b92de074cab45bcc5a7e805730a8b583104639 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 18 May 2020 22:37:03 +0300 Subject: [PATCH 31/35] interop: add some top-level doc.go --- pkg/interop/doc.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 pkg/interop/doc.go diff --git a/pkg/interop/doc.go b/pkg/interop/doc.go new file mode 100644 index 000000000..56ddc3081 --- /dev/null +++ b/pkg/interop/doc.go @@ -0,0 +1,14 @@ +/* +Package interop contains smart contract API functions. +Its subpackages can be imported into smart contracts written in Go to provide +various functionality. Upon compilation, functions from these packages will +be substituted with appropriate NeoVM system calls implemented by Neo. Usually +these system calls have additional price in NeoVM, so they're explicitly written +in the documentation of respective functions. + +Note that unless written otherwise structures defined in this packages can't be +correctly created by new() or composite literals, they should be received from +some interop functions (and then used as parameters for some other interop +functions). +*/ +package interop From f8093e415ecb8258fbf75589f6255a94e357a89c Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 19 May 2020 00:17:05 +0300 Subject: [PATCH 32/35] docs: update compiler.md, bring it up to date And add one more reference to it into the main README. --- README.md | 11 ++- docs/compiler.md | 249 ++++++++++++++++++++++++++++------------------- 2 files changed, 160 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index 1998111ea..0b775f1b4 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,16 @@ Available network flags: - `--privnet, -p` - `--testnet, -t` -#Developer notes +## Smart contract development + +Please refer to [neo-go smart contract development +workshop](https://github.com/nspcc-dev/neo-go-sc-wrkshp) that shows some +simple contracts that can be compiled/deployed/run using neo-go compiler, SDK +and private network. For details on how Go code is translated to Neo VM +bytecode and what you can and can not do in smart contract please refer to the +[compiler documentation](docs/compiler.md). + +# Developer notes Nodes have such features as [Prometheus](https://prometheus.io/docs/guides/go-application) and [Pprof](https://golang.org/pkg/net/http/pprof/) in order to have additional information about them for debugging. diff --git a/docs/compiler.md b/docs/compiler.md index a7e59652c..bd45239ef 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -2,66 +2,36 @@ The neo-go compiler compiles Go programs to bytecode that the NEO virtual machine can understand. -## Currently supported +## Language compatibility -### Go internals -- type checking -- multiple assignments -- global variables -- types int, string, byte and booleans -- struct types + method receives -- functions -- composite literals `[]int, []string, []byte` -- basic if statements -- binary expressions -- return statements -- for loops -- imports +The compiler is mostly compatible with regular Go language specification, but +there are some important deviations that you need to be aware of that make it +a dialect of Go rather than a complete port of the language: + * `make()` ane `new()` are not supported, most of the time you can substitute + them with composite literals + * there is no real distinction between different integer types, all of them + work as big.Int in Go with a limit of 256 bit in width, so you can use + `int` for just about anything. This is the way integers work in Neo VM and + adding proper Go types emulation is considered to be too costly. + * goroutines, channels and garbage collection are not supported and will + never be because emulating that aspects of Go runtime on top of Neo VM is + close to impossible + * even though `panic()` is supported, `recover()` is not, `panic` shuts the + VM down + * lambdas are not supported (#939) + * it's not possible to rename imported interop packages, they won't work this + way (#397, #913) -### Go builtins -- len -- append - -### VM API (interop layer) +## VM API (interop layer) Compiler translates interop function calls into NEO VM syscalls or (for custom -functions) into NEO VM instructions. [Refer to GoDoc](https://godoc.org/github.com/nspcc-dev/neo-go/pkg/interop) for full API documentation. - -#### Standard NEO Smart Contract API -- account -- asset -- attribute -- block -- blockchain -- contract -- engine -- header -- input -- iterator -- output -- runtime -- storage -- transaction - -#### Custom VM utility helper functions -- crypto: - - `SHA1` - - `SHA256` - - `Hash256` - - `Hash160` -- enumerator -- util: - - `Equals` (to emit `EQUALS` opcode, not needed usually) - - `FromAddress(address string) []byte` - -## Not supported -Due to the limitations of the NEO virtual machine, features listed below will not be supported. -- channels -- goroutines -- returning multiple values from functions +functions) into NEO VM instructions. [Refer to +pkg.go.dev](https://pkg.go.dev/github.com/nspcc-dev/neo-go/pkg/interop) +for full API documentation. In general it provides the same level of +functionality as Neo .net Framework library. ## Quick start -### Compile a smart contract +### Compiling ``` ./bin/neo-go contract compile -i mycontract.go @@ -73,7 +43,7 @@ By default the filename will be the name of your .go file with the .avm extensio ./bin/neo-go contract compile -i mycontract.go --out /Users/foo/bar/contract.avm ``` -### Debugging your smart contract +### Debugging You can dump the opcodes generated by the compiler with the following command: ``` @@ -83,62 +53,143 @@ You can dump the opcodes generated by the compiler with the following command: This will result in something like this: ``` -INDEX OPCODE DESC -0 0x54 PUSH4 -1 0xc5 NEWARRAY -2 0x6b TOALTSTACK -3 0x1 PUSHBYTES1 -3 0x2a * -5 0x6a DUPFROMALTSTACK -6 0x0 PUSH0 -7 0x52 PUSH2 -8 0x7a ROLL -9 0xc4 SETITEM -10 0x6a DUPFROMALTSTACK -11 0x0 PUSH0 -12 0xc3 PICKITEM -13 0x5a PUSH10 -14 0xa2 GTE -15 0x64 JMPIFNOT -16 0x7 7 -16 0x0 0 -18 0x51 PUSH1 -19 0x6c FROMALTSTACK -20 0x75 DROP -21 0x66 RET -22 0x0 PUSH0 -23 0x6c FROMALTSTACK -24 0x75 DROP -25 0x66 RET +INDEX OPCODE PARAMETER +0 INITSLOT 0500 ("\x05\x00") << +3 PUSH0 +4 REVERSEN +5 SYSCALL "\x9a\x1f\x19J" +10 NOP +11 STLOC0 +12 LDLOC0 +13 PUSH1 +14 REVERSEN +15 PUSH1 +16 PACK +17 SYSCALL "\x05\a\x92\x16" +22 NOP +23 PUSH0 +24 REVERSEN +25 SYSCALL "E\x99Z\\" +30 NOP +31 STLOC1 +32 LDLOC1 +33 PUSH1 +34 REVERSEN +35 PUSH1 +36 PACK +37 SYSCALL "\x05\a\x92\x16" +42 NOP +43 PUSH0 +44 REVERSEN +45 SYSCALL "\x87\xc3\xd2d" +50 NOP +51 STLOC2 +52 LDLOC2 +53 PUSH1 +54 REVERSEN +55 PUSH1 +56 PACK +57 SYSCALL "\x05\a\x92\x16" +62 NOP +63 PUSH0 +64 REVERSEN +65 SYSCALL "\x1dY\xe1\x19" +70 NOP +71 STLOC3 +72 LDLOC3 +73 PUSH1 +74 REVERSEN +75 PUSH1 +76 PACK +77 SYSCALL "\x05\a\x92\x16" +82 NOP +83 PUSH1 +84 RET ``` -### Test invoke a compiled contract -You can simulate a test invocation of your compiled contract by the VM, to know the total gas cost for example, with the following command: +#### Neo Smart Contract Debugger support + +It's possible to debug contracts written in Go using standard [Neo Smart +Contract Debugger](https://github.com/neo-project/neo-debugger/) which is a +part of [Neo Blockchain +Toolkit](https://github.com/neo-project/neo-blockchain-toolkit/). To do that +you need to generate debug information using `--debug` option, like this: ``` -./bin/neo-go contract testinvoke -i mycompiledcontract.avm +$ ./bin/neo-go contract compile -i contract.go -o contract.avm --debug contract.debug.json ``` -Will output something like: -``` -{ - "state": "HALT, BREAK", - "gas_consumed": "0.006", - "Stack": [ - { - "type": "Integer", - "value": "9" - } - ] -} +This file can then be used by debugger and set up to work just like for any +other supported language. + +### Deploying + +Deploying a contract to blockchain with neo-go requires a configuration file +with contract's metadata in YAML format, like the following: ``` +project: + author: Jack Smith + email: jack@example.com + version: 1.0 + name: 'Smart contract' + description: 'Even smarter than Jack himself' + hasstorage: true + hasdynamicinvocation: false + ispayable: false + returntype: ByteArray + parameters: ['String', 'Array'] +``` -At the moment this is implemented via RPC call to the remote server. +It's passed to the `deploy` command via `-c` option: + +``` +$ ./bin/neo-go contract deploy -i contract.avm -c contract.yml -e http://localhost:20331 -w wallet.json -g 0.001 +``` + +Deployment works via an RPC server, an address of which is passed via `-e` +option and should be signed using a wallet from `-w` option. More details can +be found in `deploy` command help. + +#### Neo Express support + +It's possible to deploy contracts written in Go using [Neo +Express](https://github.com/neo-project/neo-express) which is a part of [Neo +Blockchain +Toolkit](https://github.com/neo-project/neo-blockchain-toolkit/). To do that +you need to generate a different metadata file using YAML written for +deployment with neo-go. It's done in the same step with compilation via +`--config` input parameter and `--abi` output parameter, combined with debug +support the command line will look like this: + +``` +$ ./bin/neo-go contract compile -i contract.go --config contract.yml -o contract.avm --debug contract.debug.json --abi contract.abi.json +``` + +This file can then be used by toolkit to deploy contract the same way +contracts in other languagues are deployed. + + +### Invoking +You can import your contract into the standalone VM and run it there (see [VM +documentation](vm.md) for more info), but that only works for simple contracts +that don't use blockchain a lot. For more real contracts you need to deploy +them first and then do test invocations and regular invocations with `contract +testinvokefunction` and `contract invokefunction` commands (or their variants, +see `contract` command help for more details. They all work via RPC, so it's a +mandatory parameter. + +Example call (contract `f84d6a337fbc3d3a201d41da99e86b479e7a2554` with method +`balanceOf` and method's parameter `AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y` using +given RPC server and wallet and paying 0.00001 GAS for this transaction): + +``` +$ ./bin/neo-go contract invokefunction -e http://localhost:20331 -w my_wallet.json -g 0.00001 f84d6a337fbc3d3a201d41da99e86b479e7a2554 balanceOf AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y +``` ## Smart contract examples -Some examples are provided in the [examples directory](examples). +Some examples are provided in the [examples directory](../examples). ### Check if the invoker of the contract is the owning address From 44709cf6fe8fc7c83ff220563ca5c897ca5486dd Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 22 May 2020 11:49:36 +0300 Subject: [PATCH 33/35] core: make GetUnspentCoins interop return array, fix #978. --- pkg/core/interop_neo.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 0507764dc..b5bfea395 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -169,10 +170,20 @@ func txGetUnspentCoins(ic *interop.Context, v *vm.VM) error { return errors.New("value is not a transaction") } ucs, err := ic.DAO.GetUnspentCoinState(tx.Hash()) - if err != nil { + if err == storage.ErrKeyNotFound { + v.Estack().PushVal([]vm.StackItem{}) + return nil + } else if err != nil { return errors.New("no unspent coin state found") } - v.Estack().PushVal(vm.NewInteropItem(ucs)) + + items := make([]vm.StackItem, 0, len(ucs.States)) + for i := range items { + if ucs.States[i].State&state.CoinSpent == 0 { + items = append(items, vm.NewInteropItem(&ucs.States[i].Output)) + } + } + v.Estack().PushVal(items) return nil } From f39b51deee96120c08649ad001742a37ce40aa75 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 22 May 2020 12:18:03 +0300 Subject: [PATCH 34/35] compiler: restore support for GetUnspentCoins Revert a587274. --- pkg/compiler/syscall.go | 17 +++++++++-------- pkg/interop/transaction/transaction.go | 6 ++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index d93a6cf1d..c002d2bd7 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -65,14 +65,15 @@ var syscalls = map[string]map[string]string{ "GetTransaction": "Neo.Block.GetTransaction", }, "transaction": { - "GetAttributes": "Neo.Transaction.GetAttributes", - "GetHash": "Neo.Transaction.GetHash", - "GetInputs": "Neo.Transaction.GetInputs", - "GetOutputs": "Neo.Transaction.GetOutputs", - "GetReferences": "Neo.Transaction.GetReferences", - "GetScript": "Neo.InvocationTransaction.GetScript", - "GetType": "Neo.Transaction.GetType", - "GetWitnesses": "Neo.Transaction.GetWitnesses", + "GetAttributes": "Neo.Transaction.GetAttributes", + "GetHash": "Neo.Transaction.GetHash", + "GetInputs": "Neo.Transaction.GetInputs", + "GetOutputs": "Neo.Transaction.GetOutputs", + "GetReferences": "Neo.Transaction.GetReferences", + "GetScript": "Neo.InvocationTransaction.GetScript", + "GetType": "Neo.Transaction.GetType", + "GetUnspentCoins": "Neo.Transaction.GetUnspentCoins", + "GetWitnesses": "Neo.Transaction.GetWitnesses", }, "asset": { "Create": "Neo.Asset.Create", diff --git a/pkg/interop/transaction/transaction.go b/pkg/interop/transaction/transaction.go index 00d06c25e..e354d7baf 100644 --- a/pkg/interop/transaction/transaction.go +++ b/pkg/interop/transaction/transaction.go @@ -53,6 +53,12 @@ func GetReferences(t Transaction) []interface{} { return []interface{}{} } +// GetUnspentCoins returns a slice of not yet spent ouputs of a given transaction. +// This function uses `Neo.Transaction.GetUnspentCoint` syscall. +func GetUnspentCoins(t Transaction) []output.Output { + return []output.Output{} +} + // GetInputs returns a slice of inputs of a given Transaction. Refer to input // package on how to use them. This function uses `Neo.Transaction.GetInputs` // syscall. From a085bb5a3725585c4b90d376e85225db09aba47d Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 27 Apr 2020 16:24:21 +0300 Subject: [PATCH 35/35] README: update with block import examples Fix some heading along the way. --- README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b775f1b4..eae5ca8fe 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ Build the **neo-go** CLI: make build ``` +### Running + Quick start a NEO node on the private network. This requires the [neo-privatenet](https://hub.docker.com/r/cityofzion/neo-privatenet/) Docker image running on your machine. ``` @@ -92,6 +94,20 @@ Available network flags: - `--privnet, -p` - `--testnet, -t` +#### Importing mainnet/testnet dump files + +If you want to jump-start your mainnet or testnet node with [chain archives +provided by NGD](https://sync.ngd.network/) follow these instructions (when +they'd be available for 3.0 networks): +``` +$ wget .../chain.acc.zip # chain dump file +$ unzip chain.acc.zip +$ ./bin/neo-go db restore -m -i chain.acc # for testnet use '-t' flag instead of '-m' +``` + +The process differs from the C# node in that block importing is a separate +mode, after it ends the node can be started normally. + ## Smart contract development Please refer to [neo-go smart contract development @@ -114,7 +130,7 @@ In `config/protocol.*.yml` there is ``` where you can switch on/off and define port. Prometheus is enabled and Pprof is disabled by default. -# Contributing +## Contributing Feel free to contribute to this project after reading the [contributing guidelines](CONTRIBUTING.md).