Skip to content

Commit

Permalink
*: introduce stable contract hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
roman-khimov committed Nov 26, 2020
1 parent c5e39df commit 3ac2303
Show file tree
Hide file tree
Showing 32 changed files with 315 additions and 293 deletions.
10 changes: 3 additions & 7 deletions cli/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import (
"testing"

"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -58,7 +56,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {

res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
require.Equal(t, vm.HaltState.String(), res.State)
require.Equal(t, vm.HaltState.String(), res.State, res.FaultException)
require.Len(t, res.Stack, 1)
require.Equal(t, []byte("on create|sub create"), res.Stack[0].Value())

Expand All @@ -77,8 +75,6 @@ func TestComlileAndInvokeFunction(t *testing.T) {

rawNef, err := ioutil.ReadFile(nefName)
require.NoError(t, err)
realNef, err := nef.FileFromBytes(rawNef)
require.NoError(t, err)
rawManifest, err := ioutil.ReadFile(manifestName)
require.NoError(t, err)

Expand All @@ -87,15 +83,15 @@ func TestComlileAndInvokeFunction(t *testing.T) {
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr,
h.StringLE(), "update",
"bytes:"+hex.EncodeToString(realNef.Script),
"bytes:"+hex.EncodeToString(rawNef),
"bytes:"+hex.EncodeToString(rawManifest),
)
e.checkTxPersisted(t, "Sent invocation transaction ")

e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr,
hash.Hash160(realNef.Script).StringLE(), "getValue")
h.StringLE(), "getValue")

res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
Expand Down
13 changes: 10 additions & 3 deletions cli/smartcontract/smart_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/cli/paramcontext"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"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/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
Expand Down Expand Up @@ -734,13 +735,18 @@ func contractDeploy(ctx *cli.Context) error {
if err != nil {
return err
}
sender, err := address.StringToUint160(acc.Address)
if err != nil {
return cli.NewExitError(err, 1)
}
f, err := ioutil.ReadFile(in)
if err != nil {
return cli.NewExitError(err, 1)
}
// Check the file.
nefFile, err := nef.FileFromBytes(f)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to restore .nef file: %w", err), 1)
return cli.NewExitError(fmt.Errorf("failed to read .nef file: %w", err), 1)
}

manifestBytes, err := ioutil.ReadFile(manifestFile)
Expand All @@ -761,7 +767,7 @@ func contractDeploy(ctx *cli.Context) error {
return err
}

txScript, err := request.CreateDeploymentScript(nefFile.Script, m)
txScript, err := request.CreateDeploymentScript(f, m)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %w", err), 1)
}
Expand All @@ -775,7 +781,8 @@ func contractDeploy(ctx *cli.Context) error {
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
}
fmt.Fprintf(ctx.App.Writer, "Contract: %s\n", nefFile.Header.ScriptHash.StringLE())
hash := state.CreateContractHash(sender, nefFile.Script)
fmt.Fprintf(ctx.App.Writer, "Contract: %s\n", hash.StringLE())
fmt.Fprintln(ctx.App.Writer, txHash.StringLE())
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion cli/wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ func importDeployed(ctx *cli.Context) error {
if md == nil {
return cli.NewExitError("contract has no `verify` method", 1)
}
acc.Address = address.Uint160ToString(cs.ScriptHash())
acc.Address = address.Uint160ToString(cs.Hash)
acc.Contract.Script = cs.Script
acc.Contract.Parameters = acc.Contract.Parameters[:0]
for _, p := range md.Parameters {
Expand Down
37 changes: 29 additions & 8 deletions internal/testchain/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import (
gio "io"

"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"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/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
Expand Down Expand Up @@ -46,28 +49,46 @@ func NewTransferFromOwner(bc blockchainer.Blockchainer, contractHash, to util.Ui
}

// NewDeployTx returns new deployment tx for contract with name with Go code read from r.
func NewDeployTx(name string, r gio.Reader) (*transaction.Transaction, []byte, error) {
func NewDeployTx(name string, sender util.Uint160, r gio.Reader) (*transaction.Transaction, util.Uint160, error) {
// nef.NewFile() cares about version a lot.
config.Version = "0.90.0-test"

avm, di, err := compiler.CompileWithDebugInfo(name, r)
if err != nil {
return nil, nil, err
return nil, util.Uint160{}, err
}

ne, err := nef.NewFile(avm)
if err != nil {
return nil, util.Uint160{}, err
}
neb, err := ne.Bytes()
if err != nil {
return nil, util.Uint160{}, err
}

w := io.NewBufBinWriter()
m, err := di.ConvertToManifest(name, nil)
if err != nil {
return nil, nil, err
return nil, util.Uint160{}, err
}
bs, err := json.Marshal(m)
if err != nil {
return nil, nil, err
return nil, util.Uint160{}, err
}

w := io.NewBufBinWriter()
emit.Bytes(w.BinWriter, bs)
emit.Bytes(w.BinWriter, avm)
emit.Bytes(w.BinWriter, neb)
emit.Syscall(w.BinWriter, interopnames.SystemContractCreate)
if w.Err != nil {
return nil, nil, err
return nil, util.Uint160{}, w.Err
}
return transaction.New(Network(), w.Bytes(), 100*native.GASFactor), avm, nil
txScript := w.Bytes()
tx := transaction.New(Network(), txScript, 100*native.GASFactor)
tx.Signers = []transaction.Signer{{Account: sender}}
h := state.CreateContractHash(tx.Sender(), avm)

return tx, h, nil
}

// SignTx signs provided transactions with validator keys.
Expand Down
1 change: 1 addition & 0 deletions pkg/compiler/interop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func TestAppCall(t *testing.T) {
ih := hash.Hash160(inner)
ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false), nil, nil, nil, zaptest.NewLogger(t))
require.NoError(t, ic.DAO.PutContractState(&state.Contract{
Hash: ih,
Script: inner,
Manifest: *m,
}))
Expand Down
12 changes: 6 additions & 6 deletions pkg/core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -940,25 +940,25 @@ func TestVerifyHashAgainstScript(t *testing.T) {
gas := bc.contracts.Policy.GetMaxVerificationGas(ic.DAO)
t.Run("Contract", func(t *testing.T) {
t.Run("Missing", func(t *testing.T) {
newH := cs.ScriptHash()
newH := cs.Hash
newH[0] = ^newH[0]
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
_, err := bc.verifyHashAgainstScript(newH, w, ic, gas)
require.True(t, errors.Is(err, ErrUnknownVerificationContract))
})
t.Run("Invalid", func(t *testing.T) {
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
_, err := bc.verifyHashAgainstScript(csInvalid.ScriptHash(), w, ic, gas)
_, err := bc.verifyHashAgainstScript(csInvalid.Hash, w, ic, gas)
require.True(t, errors.Is(err, ErrInvalidVerificationContract))
})
t.Run("ValidSignature", func(t *testing.T) {
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
_, err := bc.verifyHashAgainstScript(cs.ScriptHash(), w, ic, gas)
_, err := bc.verifyHashAgainstScript(cs.Hash, w, ic, gas)
require.NoError(t, err)
})
t.Run("InvalidSignature", func(t *testing.T) {
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH3)}}
_, err := bc.verifyHashAgainstScript(cs.ScriptHash(), w, ic, gas)
_, err := bc.verifyHashAgainstScript(cs.Hash, w, ic, gas)
require.True(t, errors.Is(err, ErrVerificationFailed))
})
})
Expand Down Expand Up @@ -1071,7 +1071,7 @@ func TestIsTxStillRelevant(t *testing.T) {
currentHeight := blockchain.GetHeight()
return currentHeight < %d
}`, bc.BlockHeight()+2) // deploy + next block
txDeploy, avm, err := testchain.NewDeployTx("TestVerify", strings.NewReader(src))
txDeploy, h, err := testchain.NewDeployTx("TestVerify", neoOwner, strings.NewReader(src))
require.NoError(t, err)
txDeploy.ValidUntilBlock = bc.BlockHeight() + 1
addSigners(txDeploy)
Expand All @@ -1080,7 +1080,7 @@ func TestIsTxStillRelevant(t *testing.T) {

tx := newTx(t)
tx.Signers = append(tx.Signers, transaction.Signer{
Account: hash.Hash160(avm),
Account: h,
Scopes: transaction.None,
})
tx.NetworkFee += 1_000_000
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/dao/cacheddao.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (cd *Cached) GetContractState(hash util.Uint160) (*state.Contract, error) {

// PutContractState puts given contract state into the given store.
func (cd *Cached) PutContractState(cs *state.Contract) error {
cd.contracts[cs.ScriptHash()] = cs
cd.contracts[cs.Hash] = cs
return cd.DAO.PutContractState(cs)
}

Expand Down
1 change: 1 addition & 0 deletions pkg/core/dao/cacheddao_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func TestCachedDaoContracts(t *testing.T) {

cs := &state.Contract{
ID: 123,
Hash: sh,
Script: script,
Manifest: *m,
}
Expand Down
7 changes: 2 additions & 5 deletions pkg/core/dao/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,13 @@ func (dao *Simple) GetContractState(hash util.Uint160) (*state.Contract, error)
if err != nil {
return nil, err
}
if contract.ScriptHash() != hash {
return nil, fmt.Errorf("found script hash is not equal to expected")
}

return contract, nil
}

// PutContractState puts given contract state into the given store.
func (dao *Simple) PutContractState(cs *state.Contract) error {
key := storage.AppendPrefix(storage.STContract, cs.ScriptHash().BytesBE())
key := storage.AppendPrefix(storage.STContract, cs.Hash.BytesBE())
if err := dao.Put(cs, key); err != nil {
return err
}
Expand Down Expand Up @@ -180,7 +177,7 @@ func (dao *Simple) putContractScriptHash(cs *state.Contract) error {
key := make([]byte, 5)
key[0] = byte(storage.STContractID)
binary.LittleEndian.PutUint32(key[1:], uint32(cs.ID))
return dao.Store.Put(key, cs.ScriptHash().BytesBE())
return dao.Store.Put(key, cs.Hash.BytesBE())
}

// GetContractScriptHash returns script hash of the contract with the specified ID.
Expand Down
17 changes: 10 additions & 7 deletions pkg/core/dao/dao_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"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/hash"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
Expand Down Expand Up @@ -44,24 +45,26 @@ func (t *TestSerializable) DecodeBinary(reader *io.BinReader) {

func TestPutAndGetContractState(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
contractState := &state.Contract{Script: []byte{}}
hash := contractState.ScriptHash()
script := []byte{}
h := hash.Hash160(script)
contractState := &state.Contract{Hash: h, Script: script}
err := dao.PutContractState(contractState)
require.NoError(t, err)
gotContractState, err := dao.GetContractState(hash)
gotContractState, err := dao.GetContractState(contractState.Hash)
require.NoError(t, err)
require.Equal(t, contractState, gotContractState)
}

func TestDeleteContractState(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
contractState := &state.Contract{Script: []byte{}}
hash := contractState.ScriptHash()
script := []byte{}
h := hash.Hash160(script)
contractState := &state.Contract{Hash: h, Script: script}
err := dao.PutContractState(contractState)
require.NoError(t, err)
err = dao.DeleteContractState(hash)
err = dao.DeleteContractState(h)
require.NoError(t, err)
gotContractState, err := dao.GetContractState(hash)
gotContractState, err := dao.GetContractState(h)
require.Error(t, err)
require.Nil(t, gotContractState)
}
Expand Down
25 changes: 10 additions & 15 deletions pkg/core/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,9 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, err)

// Push some contract into the chain.
txDeploy, avm := newDeployTx(t, prefix+"test_contract.go", "Rubl")
txDeploy, cHash := newDeployTx(t, priv0ScriptHash, prefix+"test_contract.go", "Rubl")
txDeploy.Nonce = getNextNonce()
txDeploy.ValidUntilBlock = validUntilBlock
txDeploy.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
require.NoError(t, acc0.SignTx(txDeploy))
b = bc.newBlock(txDeploy)
Expand All @@ -285,7 +284,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {

// Now invoke this contract.
script := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "putValue", "testkey", "testvalue")
emit.AppCallWithOperationAndArgs(script.BinWriter, cHash, "putValue", "testkey", "testvalue")

txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor)
txInv.Nonce = getNextNonce()
Expand Down Expand Up @@ -314,16 +313,15 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
b = bc.newBlock(txNeo0to1)
require.NoError(t, bc.AddBlock(b))

sh := hash.Hash160(avm)
w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, sh, "init")
emit.AppCallWithOperationAndArgs(w.BinWriter, cHash, "init")
initTx := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor)
initTx.Nonce = getNextNonce()
initTx.ValidUntilBlock = validUntilBlock
initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
require.NoError(t, addNetworkFee(bc, initTx, acc0))
require.NoError(t, acc0.SignTx(initTx))
transferTx := newNEP17Transfer(sh, sh, priv0.GetScriptHash(), 1000)
transferTx := newNEP17Transfer(cHash, cHash, priv0.GetScriptHash(), 1000)
transferTx.Nonce = getNextNonce()
transferTx.ValidUntilBlock = validUntilBlock
transferTx.Signers = []transaction.Signer{
Expand All @@ -341,7 +339,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, bc.AddBlock(b))
t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE())

transferTx = newNEP17Transfer(sh, priv0.GetScriptHash(), priv1.GetScriptHash(), 123)
transferTx = newNEP17Transfer(cHash, priv0.GetScriptHash(), priv1.GetScriptHash(), 123)
transferTx.Nonce = getNextNonce()
transferTx.ValidUntilBlock = validUntilBlock
transferTx.Signers = []transaction.Signer{
Expand All @@ -360,10 +358,9 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())

// Push verification contract into the chain.
txDeploy2, _ := newDeployTx(t, prefix+"verification_contract.go", "Verify")
txDeploy2, _ := newDeployTx(t, priv0ScriptHash, prefix+"verification_contract.go", "Verify")
txDeploy2.Nonce = getNextNonce()
txDeploy2.ValidUntilBlock = validUntilBlock
txDeploy2.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
require.NoError(t, acc0.SignTx(txDeploy2))
b = bc.newBlock(txDeploy2)
Expand All @@ -379,15 +376,13 @@ func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ..
return transaction.New(testchain.Network(), script, 10000000)
}

func newDeployTx(t *testing.T, name, ctrName string) (*transaction.Transaction, []byte) {
func newDeployTx(t *testing.T, sender util.Uint160, name, ctrName string) (*transaction.Transaction, util.Uint160) {
c, err := ioutil.ReadFile(name)
require.NoError(t, err)
tx, avm, err := testchain.NewDeployTx(ctrName, bytes.NewReader(c))
tx, h, err := testchain.NewDeployTx(ctrName, sender, bytes.NewReader(c))
require.NoError(t, err)
t.Logf("contractHash (%s): %s", name, hash.Hash160(avm).StringLE())
t.Logf("contractScript: %x", avm)

return tx, avm
t.Logf("contractHash (%s): %s", name, h.StringLE())
return tx, h
}

func addSigners(txs ...*transaction.Transaction) {
Expand Down
Loading

0 comments on commit 3ac2303

Please sign in to comment.