Skip to content

Commit

Permalink
native: add Ledger contract, fix #1696
Browse files Browse the repository at this point in the history
But don't change the way we process/store transactions and blocks. Effectively
it's just an interface for smart contracts that replaces old syscalls.

Transaction definition is moved temporarily to runtime package and Block
definition is removed (till we solve #1691 properly).
  • Loading branch information
roman-khimov committed Feb 3, 2021
1 parent 641896b commit 0981516
Show file tree
Hide file tree
Showing 24 changed files with 470 additions and 445 deletions.
Binary file modified cli/testdata/chain50x2.acc
Binary file not shown.
2 changes: 1 addition & 1 deletion pkg/compiler/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func scAndVMInteropTypeFromExpr(named *types.Named) (smartcontract.ParamType, st
name := named.Obj().Name()
pkg := named.Obj().Pkg().Name()
switch pkg {
case "blockchain", "contract":
case "runtime", "contract":
return smartcontract.ArrayType, stackitem.ArrayT // Block, Transaction, Contract
case "interop":
if name != "Interface" {
Expand Down
4 changes: 2 additions & 2 deletions pkg/compiler/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestCodeGen_DebugInfo(t *testing.T) {
src := `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop"
import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func Main(op string) bool {
var s string
_ = s
Expand Down Expand Up @@ -47,7 +47,7 @@ func unexportedMethod() int { return 1 }
func MethodParams(addr interop.Hash160, h interop.Hash256,
sig interop.Signature, pub interop.PublicKey,
inter interop.Interface,
ctx storage.Context, tx blockchain.Transaction) bool {
ctx storage.Context, tx runtime.Transaction) bool {
return true
}
type MyStruct struct {}
Expand Down
7 changes: 0 additions & 7 deletions pkg/compiler/syscall.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,6 @@ var syscalls = map[string]map[string]string{
"Itoa": interopnames.SystemBinaryItoa,
"Serialize": interopnames.SystemBinarySerialize,
},
"blockchain": {
"GetBlock": interopnames.SystemBlockchainGetBlock,
"GetHeight": interopnames.SystemBlockchainGetHeight,
"GetTransaction": interopnames.SystemBlockchainGetTransaction,
"GetTransactionFromBlock": interopnames.SystemBlockchainGetTransactionFromBlock,
"GetTransactionHeight": interopnames.SystemBlockchainGetTransactionHeight,
},
"contract": {
"Call": interopnames.SystemContractCall,
"CreateStandardAccount": interopnames.SystemContractCreateStandardAccount,
Expand Down
15 changes: 9 additions & 6 deletions pkg/core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package core

import (
"errors"
"fmt"
"math/big"
"math/rand"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -1165,12 +1163,16 @@ func TestIsTxStillRelevant(t *testing.T) {
require.NoError(t, bc.AddBlock(bc.newBlock()))
require.True(t, bc.IsTxStillRelevant(tx3, nil, false))
})
/* // neo-project/neo#2289
t.Run("contract witness check fails", func(t *testing.T) {
src := fmt.Sprintf(`package verify
import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
import (
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
)
func Verify() bool {
currentHeight := blockchain.GetHeight()
return currentHeight < %d
currentHeight := contract.Call(util.FromAddress("NV5WuMGkwhQexQ4afTwuRojMeWwfWrEAdv"), "currentIndex", contract.ReadStates)
return currentHeight.(int) < %d
}`, bc.BlockHeight()+2) // deploy + next block
txDeploy, h, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src))
require.NoError(t, err)
Expand All @@ -1184,14 +1186,15 @@ func TestIsTxStillRelevant(t *testing.T) {
Account: h,
Scopes: transaction.None,
})
tx.NetworkFee += 1_000_000
tx.NetworkFee += 10_000_000
require.NoError(t, testchain.SignTx(bc, tx))
tx.Scripts = append(tx.Scripts, transaction.Witness{})
require.True(t, bc.IsTxStillRelevant(tx, mp, false))
require.NoError(t, bc.AddBlock(bc.newBlock()))
require.False(t, bc.IsTxStillRelevant(tx, mp, false))
})
*/
}

func TestMemPoolRemoval(t *testing.T) {
Expand Down
10 changes: 0 additions & 10 deletions pkg/core/interop/interopnames/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ const (
SystemBinaryDeserialize = "System.Binary.Deserialize"
SystemBinaryItoa = "System.Binary.Itoa"
SystemBinarySerialize = "System.Binary.Serialize"
SystemBlockchainGetBlock = "System.Blockchain.GetBlock"
SystemBlockchainGetHeight = "System.Blockchain.GetHeight"
SystemBlockchainGetTransaction = "System.Blockchain.GetTransaction"
SystemBlockchainGetTransactionFromBlock = "System.Blockchain.GetTransactionFromBlock"
SystemBlockchainGetTransactionHeight = "System.Blockchain.GetTransactionHeight"
SystemCallbackCreate = "System.Callback.Create"
SystemCallbackCreateFromMethod = "System.Callback.CreateFromMethod"
SystemCallbackCreateFromSyscall = "System.Callback.CreateFromSyscall"
Expand Down Expand Up @@ -69,11 +64,6 @@ var names = []string{
SystemBinaryDeserialize,
SystemBinaryItoa,
SystemBinarySerialize,
SystemBlockchainGetBlock,
SystemBlockchainGetHeight,
SystemBlockchainGetTransaction,
SystemBlockchainGetTransactionFromBlock,
SystemBlockchainGetTransactionHeight,
SystemCallbackCreate,
SystemCallbackCreateFromMethod,
SystemCallbackCreateFromSyscall,
Expand Down
138 changes: 3 additions & 135 deletions pkg/core/interop_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ import (
"crypto/elliptic"
"errors"
"fmt"
"math"
"math/big"

"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
Expand Down Expand Up @@ -45,144 +42,15 @@ const (
Constant StorageFlag = 0x01
)

// getBlockHashFromElement converts given vm.Element to block hash using given
// Blockchainer if needed. Interop functions accept both block numbers and
// block hashes as parameters, thus this function is needed.
func getBlockHashFromElement(bc blockchainer.Blockchainer, element *vm.Element) (util.Uint256, error) {
var hash util.Uint256
hashbytes := element.Bytes()
if len(hashbytes) <= 5 {
hashint := element.BigInt().Int64()
if hashint < 0 || hashint > math.MaxUint32 {
return hash, errors.New("bad block index")
}
hash = bc.GetHeaderHash(int(hashint))
} else {
return util.Uint256DecodeBytesBE(hashbytes)
}
return hash, nil
}

// blockToStackItem converts block.Block to stackitem.Item
func blockToStackItem(b *block.Block) stackitem.Item {
return stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(b.Hash().BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(b.Version))),
stackitem.NewByteArray(b.PrevHash.BytesBE()),
stackitem.NewByteArray(b.MerkleRoot.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(b.Timestamp))),
stackitem.NewBigInteger(big.NewInt(int64(b.Index))),
stackitem.NewByteArray(b.NextConsensus.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))),
})
}

// bcGetBlock returns current block.
func bcGetBlock(ic *interop.Context) error {
hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop())
if err != nil {
return err
}
block, err := ic.Chain.GetBlock(hash)
if err != nil || !isTraceableBlock(ic, block.Index) {
ic.VM.Estack().PushVal(stackitem.Null{})
} else {
ic.VM.Estack().PushVal(blockToStackItem(block))
}
return nil
}

// bcGetHeight returns blockchain height.
func bcGetHeight(ic *interop.Context) error {
ic.VM.Estack().PushVal(ic.Chain.BlockHeight())
return nil
}

// getTransactionAndHeight gets parameter from the vm evaluation stack and
// returns transaction and its height if it's present in the blockchain.
func getTransactionAndHeight(cd *dao.Cached, v *vm.VM) (*transaction.Transaction, uint32, error) {
hashbytes := v.Estack().Pop().Bytes()
hash, err := util.Uint256DecodeBytesBE(hashbytes)
if err != nil {
return nil, 0, err
}
return cd.GetTransaction(hash)
}

// isTraceableBlock defines whether we're able to give information about
// the block with index specified.
func isTraceableBlock(ic *interop.Context, index uint32) bool {
height := ic.Chain.BlockHeight()
MaxTraceableBlocks := ic.Chain.GetConfig().MaxTraceableBlocks
return index <= height && index+MaxTraceableBlocks > height
}

// transactionToStackItem converts transaction.Transaction to stackitem.Item
func transactionToStackItem(t *transaction.Transaction) stackitem.Item {
return stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(t.Hash().BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(t.Version))),
stackitem.NewBigInteger(big.NewInt(int64(t.Nonce))),
stackitem.NewByteArray(t.Sender().BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(t.SystemFee))),
stackitem.NewBigInteger(big.NewInt(int64(t.NetworkFee))),
stackitem.NewBigInteger(big.NewInt(int64(t.ValidUntilBlock))),
stackitem.NewByteArray(t.Script),
})
}

// bcGetTransaction returns transaction.
func bcGetTransaction(ic *interop.Context) error {
tx, h, err := getTransactionAndHeight(ic.DAO, ic.VM)
if err != nil || !isTraceableBlock(ic, h) {
ic.VM.Estack().PushVal(stackitem.Null{})
return nil
}
ic.VM.Estack().PushVal(transactionToStackItem(tx))
return nil
}

// bcGetTransactionFromBlock returns transaction with the given index from the
// block with height or hash specified.
func bcGetTransactionFromBlock(ic *interop.Context) error {
hash, err := getBlockHashFromElement(ic.Chain, ic.VM.Estack().Pop())
if err != nil {
return err
}
index := ic.VM.Estack().Pop().BigInt().Int64()
block, err := ic.DAO.GetBlock(hash)
if err != nil || !isTraceableBlock(ic, block.Index) {
ic.VM.Estack().PushVal(stackitem.Null{})
return nil
}
if index < 0 || index >= int64(len(block.Transactions)) {
return errors.New("wrong transaction index")
}
tx := block.Transactions[index]
ic.VM.Estack().PushVal(tx.Hash().BytesBE())
return nil
}

// bcGetTransactionHeight returns transaction height.
func bcGetTransactionHeight(ic *interop.Context) error {
_, h, err := getTransactionAndHeight(ic.DAO, ic.VM)
if err != nil || !isTraceableBlock(ic, h) {
ic.VM.Estack().PushVal(-1)
return nil
}
ic.VM.Estack().PushVal(h)
return nil
}

// engineGetScriptContainer returns transaction or block that contains the script
// being run.
func engineGetScriptContainer(ic *interop.Context) error {
var item stackitem.Item
switch t := ic.Container.(type) {
case *transaction.Transaction:
item = transactionToStackItem(t)
item = native.TransactionToStackItem(t)
case *block.Block:
item = blockToStackItem(t)
item = native.BlockToStackItem(t)
default:
return errors.New("unknown script container")
}
Expand Down
Loading

0 comments on commit 0981516

Please sign in to comment.