state: use checksums and names to calculate contract hashes
It allows to deploy the same NEF using one sender and get different contract hashes. See neo-project/neo#2240.
This commit is contained in:
parent
6b9b37f170
commit
054ca27e9c
14 changed files with 76 additions and 39 deletions
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"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"
|
||||
|
@ -33,28 +34,37 @@ func TestCalcHash(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
nefF, err := nef.FileFromBytes(src)
|
||||
require.NoError(t, err)
|
||||
manifestPath := "./testdata/verify.manifest.json"
|
||||
manifestBytes, err := ioutil.ReadFile(manifestPath)
|
||||
require.NoError(t, err)
|
||||
manif := &manifest.Manifest{}
|
||||
err = json.Unmarshal(manifestBytes, manif)
|
||||
require.NoError(t, err)
|
||||
sender := random.Uint160()
|
||||
|
||||
cmd := []string{"neo-go", "contract", "calc-hash"}
|
||||
t.Run("no sender", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "--in", nefPath)...)
|
||||
e.RunWithError(t, append(cmd, "--in", nefPath, "--manifest", manifestPath)...)
|
||||
})
|
||||
t.Run("no nef file", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "--sender", sender.StringLE())...)
|
||||
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--manifest", manifestPath)...)
|
||||
})
|
||||
t.Run("no manifest file", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", nefPath)...)
|
||||
})
|
||||
t.Run("invalid path", func(t *testing.T) {
|
||||
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(),
|
||||
"--in", "./testdata/verify.nef123")...)
|
||||
"--in", "./testdata/verify.nef123", "--manifest", manifestPath)...)
|
||||
})
|
||||
t.Run("invalid file", func(t *testing.T) {
|
||||
p := path.Join(os.TempDir(), "neogo.calchash.verify.nef")
|
||||
defer os.Remove(p)
|
||||
require.NoError(t, ioutil.WriteFile(p, src[:4], os.ModePerm))
|
||||
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", p)...)
|
||||
e.RunWithError(t, append(cmd, "--sender", sender.StringLE(), "--in", p, "--manifest", manifestPath)...)
|
||||
})
|
||||
|
||||
cmd = append(cmd, "--in", nefPath)
|
||||
expected := state.CreateContractHash(sender, nefF.Script)
|
||||
cmd = append(cmd, "--in", nefPath, "--manifest", manifestPath)
|
||||
expected := state.CreateContractHash(sender, nefF.Checksum, manif.Name)
|
||||
t.Run("valid, uint160", func(t *testing.T) {
|
||||
e.Run(t, append(cmd, "--sender", sender.StringLE())...)
|
||||
e.checkNextLine(t, expected.StringLE())
|
||||
|
@ -193,7 +203,8 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
|||
|
||||
t.Run("check calc hash", func(t *testing.T) {
|
||||
e.Run(t, "neo-go", "contract", "calc-hash",
|
||||
"--sender", validatorAddr, "--in", nefName)
|
||||
"--sender", validatorAddr, "--in", nefName,
|
||||
"--manifest", manifestName)
|
||||
e.checkNextLine(t, h.StringLE())
|
||||
})
|
||||
|
||||
|
|
|
@ -351,6 +351,10 @@ func NewCommands() []cli.Command {
|
|||
Name: "in",
|
||||
Usage: "path to NEF file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "manifest, m",
|
||||
Usage: "path to manifest file",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -473,6 +477,10 @@ func calcHash(ctx *cli.Context) error {
|
|||
if p == "" {
|
||||
return cli.NewExitError(errors.New("no .nef file was provided"), 1)
|
||||
}
|
||||
mpath := ctx.String("manifest")
|
||||
if mpath == "" {
|
||||
return cli.NewExitError(errors.New("no manifest file provided"), 1)
|
||||
}
|
||||
f, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't read .nef file: %w", err), 1)
|
||||
|
@ -481,7 +489,16 @@ func calcHash(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't unmarshal .nef file: %w", err), 1)
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, "Contract hash:", state.CreateContractHash(u, nefFile.Script).StringLE())
|
||||
manifestBytes, err := ioutil.ReadFile(mpath)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1)
|
||||
}
|
||||
m := &manifest.Manifest{}
|
||||
err = json.Unmarshal(manifestBytes, m)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to restore manifest file: %w", err), 1)
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, "Contract hash:", state.CreateContractHash(u, nefFile.Checksum, m.Name).StringLE())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -842,7 +859,7 @@ func contractDeploy(ctx *cli.Context) error {
|
|||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
|
||||
}
|
||||
hash := state.CreateContractHash(sender, nefFile.Script)
|
||||
hash := state.CreateContractHash(sender, nefFile.Checksum, m.Name)
|
||||
fmt.Fprintf(ctx.App.Writer, "Contract: %s\n", hash.StringLE())
|
||||
fmt.Fprintln(ctx.App.Writer, txHash.StringLE())
|
||||
return nil
|
||||
|
|
BIN
cli/testdata/chain50x2.acc
vendored
BIN
cli/testdata/chain50x2.acc
vendored
Binary file not shown.
|
@ -84,7 +84,7 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160,
|
|||
|
||||
tx := transaction.New(Network(), buf.Bytes(), 100*native.GASFactor)
|
||||
tx.Signers = []transaction.Signer{{Account: sender}}
|
||||
h := state.CreateContractHash(tx.Sender(), avm)
|
||||
h := state.CreateContractHash(tx.Sender(), ne.Checksum, name)
|
||||
|
||||
return tx, h, nil
|
||||
}
|
||||
|
|
|
@ -556,11 +556,11 @@ func TestVerifyTx(t *testing.T) {
|
|||
})
|
||||
tx.Scripts = append(tx.Scripts, transaction.Witness{})
|
||||
t.Run("NonZeroVerification", func(t *testing.T) {
|
||||
script, _ := state.CreateNativeContractHash(-6) //oracleContractID
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
||||
emit.Bytes(w.BinWriter, util.Uint160{}.BytesBE())
|
||||
emit.Bytes(w.BinWriter, script)
|
||||
emit.Int(w.BinWriter, int64(orc.NEF.Checksum))
|
||||
emit.String(w.BinWriter, orc.Manifest.Name)
|
||||
tx.Scripts[len(tx.Scripts)-1].VerificationScript = w.Bytes()
|
||||
err := bc.VerifyTx(tx)
|
||||
require.True(t, errors.Is(err, ErrNativeContractWitness), "got: %v", err)
|
||||
|
|
|
@ -119,8 +119,9 @@ func NewContractMD(name string, id int32) *ContractMD {
|
|||
// Therefore values are taken from C# node.
|
||||
c.NEF.Header.Compiler = "neo-core-v3.0"
|
||||
c.NEF.Header.Magic = nef.Magic
|
||||
c.NEF.Script, c.Hash = state.CreateNativeContractHash(id)
|
||||
c.NEF.Script = state.CreateNativeContractScript(id)
|
||||
c.NEF.Checksum = c.NEF.CalculateChecksum()
|
||||
c.Hash = state.CreateContractHash(util.Uint160{}, c.NEF.Checksum, name)
|
||||
c.Manifest = *manifest.DefaultManifest(name)
|
||||
|
||||
return c
|
||||
|
|
|
@ -8,12 +8,12 @@ import (
|
|||
|
||||
// Compatibility test. hashes are taken directly from C# node.
|
||||
func TestNativeHashes(t *testing.T) {
|
||||
require.Equal(t, "bee421fdbb3e791265d2104cb34934f53fcc0e45", newManagement().Hash.StringLE())
|
||||
require.Equal(t, "4961bf0ab79370b23dc45cde29f568d0e0fa6e93", newNEO().Hash.StringLE())
|
||||
require.Equal(t, "9ac04cf223f646de5f7faccafe34e30e5d4382a2", newGAS().Hash.StringLE())
|
||||
require.Equal(t, "c939a4af1c762e5edca36d4b61c06ba82c4c6ff5", newPolicy().Hash.StringLE())
|
||||
require.Equal(t, "f4bbd95569e8dda2cb84eb609a1488ddd0d9fa91", newDesignate(false).Hash.StringLE())
|
||||
require.Equal(t, "8cd3889136056b3304ec59f6d424b8767710ed79", newOracle().Hash.StringLE())
|
||||
require.Equal(t, "a501d7d7d10983673b61b7a2d3a813b36f9f0e43", newManagement().Hash.StringLE())
|
||||
require.Equal(t, "f617baca689d1abddedda7c3b80675c4ac21e932", newNEO().Hash.StringLE())
|
||||
require.Equal(t, "75844530eb44f4715a42950bb59b4d7ace0b2f3d", newGAS().Hash.StringLE())
|
||||
require.Equal(t, "e21a28cfc1e662e152f668c86198141cc17b6c37", newPolicy().Hash.StringLE())
|
||||
require.Equal(t, "69b1909aaa14143b0624ba0d61d5cd3b8b67529d", newDesignate(false).Hash.StringLE())
|
||||
require.Equal(t, "b82bbf650f963dbf71577d10ea4077e711a13e7b", newOracle().Hash.StringLE())
|
||||
// Not yet a part of NEO.
|
||||
//require.Equal(t, "", newNotary().Hash.StringLE()())
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ func (m *Management) markUpdated(h util.Uint160) {
|
|||
// Deploy creates contract's hash/ID and saves new contract into the given DAO.
|
||||
// It doesn't run _deploy method and doesn't emit notification.
|
||||
func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) {
|
||||
h := state.CreateContractHash(sender, neff.Script)
|
||||
h := state.CreateContractHash(sender, neff.Checksum, manif.Name)
|
||||
key := makeContractKey(h)
|
||||
si := d.GetStorageItem(m.ContractID, key)
|
||||
if si != nil {
|
||||
|
|
|
@ -18,13 +18,13 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
|||
d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
||||
script := []byte{1}
|
||||
sender := util.Uint160{1, 2, 3}
|
||||
h := state.CreateContractHash(sender, script)
|
||||
|
||||
ne, err := nef.NewFile(script)
|
||||
require.NoError(t, err)
|
||||
manif := manifest.NewManifest("Test")
|
||||
require.NoError(t, err)
|
||||
|
||||
h := state.CreateContractHash(sender, ne.Checksum, manif.Name)
|
||||
|
||||
contract, err := mgmt.Deploy(d, sender, ne, manif)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int32(1), contract.ID)
|
||||
|
@ -43,7 +43,7 @@ func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.Equal(t, int32(2), contract2.ID)
|
||||
require.Equal(t, uint16(0), contract2.UpdateCounter)
|
||||
require.Equal(t, state.CreateContractHash(sender2, script), contract2.Hash)
|
||||
require.Equal(t, state.CreateContractHash(sender2, ne.Checksum, manif.Name), contract2.Hash)
|
||||
require.Equal(t, ne, &contract2.NEF)
|
||||
require.Equal(t, *manif, contract2.Manifest)
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ func TestRestoreAfterDeploy(t *testing.T) {
|
|||
mgmtHash := bc.ManagementContractHash()
|
||||
cs1, _ := getTestContractState(bc)
|
||||
cs1.ID = 1
|
||||
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.NEF.Script)
|
||||
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.NEF.Checksum, cs1.Manifest.Name)
|
||||
manif1, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nef1, err := nef.NewFile(cs1.NEF.Script)
|
||||
|
@ -80,7 +80,7 @@ func TestContractDeploy(t *testing.T) {
|
|||
mgmtHash := bc.ManagementContractHash()
|
||||
cs1, _ := getTestContractState(bc)
|
||||
cs1.ID = 1
|
||||
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.NEF.Script)
|
||||
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.NEF.Checksum, cs1.Manifest.Name)
|
||||
manif1, err := json.Marshal(cs1.Manifest)
|
||||
require.NoError(t, err)
|
||||
nef1b, err := cs1.NEF.Bytes()
|
||||
|
@ -214,7 +214,7 @@ func TestContractDeploy(t *testing.T) {
|
|||
checkFAULTState(t, res)
|
||||
|
||||
t.Run("get after failed deploy", func(t *testing.T) {
|
||||
h := state.CreateContractHash(neoOwner, deployScript)
|
||||
h := state.CreateContractHash(neoOwner, nefD.Checksum, m.Name)
|
||||
checkContractState(t, bc, h, nil)
|
||||
})
|
||||
})
|
||||
|
@ -243,7 +243,7 @@ func TestContractDeploy(t *testing.T) {
|
|||
checkFAULTState(t, res)
|
||||
|
||||
t.Run("get after bad _deploy", func(t *testing.T) {
|
||||
h := state.CreateContractHash(neoOwner, deployScript)
|
||||
h := state.CreateContractHash(neoOwner, nefD.Checksum, m.Name)
|
||||
checkContractState(t, bc, h, nil)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -112,25 +112,25 @@ func (c *Contract) FromStackItem(item stackitem.Item) error {
|
|||
|
||||
// CreateContractHash creates deployed contract hash from transaction sender
|
||||
// and contract script.
|
||||
func CreateContractHash(sender util.Uint160, script []byte) util.Uint160 {
|
||||
func CreateContractHash(sender util.Uint160, checksum uint32, name string) util.Uint160 {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
||||
emit.Bytes(w.BinWriter, sender.BytesBE())
|
||||
emit.Bytes(w.BinWriter, script)
|
||||
emit.Int(w.BinWriter, int64(checksum))
|
||||
emit.String(w.BinWriter, name)
|
||||
if w.Err != nil {
|
||||
panic(w.Err)
|
||||
}
|
||||
return hash.Hash160(w.Bytes())
|
||||
}
|
||||
|
||||
// CreateNativeContractHash returns script and hash for the native contract.
|
||||
func CreateNativeContractHash(id int32) ([]byte, util.Uint160) {
|
||||
// CreateNativeContractScript returns script for the native contract.
|
||||
func CreateNativeContractScript(id int32) []byte {
|
||||
w := io.NewBufBinWriter()
|
||||
emit.Int(w.BinWriter, int64(id))
|
||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
||||
if w.Err != nil {
|
||||
panic(w.Err)
|
||||
}
|
||||
script := w.Bytes()
|
||||
return script, CreateContractHash(util.Uint160{}, script)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
|
|
@ -62,14 +62,22 @@ func TestEncodeDecodeContractState(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateContractHash(t *testing.T) {
|
||||
var script = []byte{1, 2, 3}
|
||||
var neff = nef.File{
|
||||
Header: nef.Header{
|
||||
Compiler: "test",
|
||||
Magic: nef.Magic,
|
||||
},
|
||||
Tokens: []nef.MethodToken{},
|
||||
Script: []byte{1, 2, 3},
|
||||
}
|
||||
var sender util.Uint160
|
||||
var err error
|
||||
|
||||
require.Equal(t, "b8e95ff7b11c427c29355e3398722d97bd2ca069", CreateContractHash(sender, script).StringLE())
|
||||
neff.Checksum = neff.CalculateChecksum()
|
||||
require.Equal(t, "9b9628e4f1611af90e761eea8cc21372380c74b6", CreateContractHash(sender, neff.Checksum, "").StringLE())
|
||||
sender, err = util.Uint160DecodeStringLE("a400ff00ff00ff00ff00ff00ff00ff00ff00ff01")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "435c467b8e15cb9b1474ad7ee817ffdcfededef9", CreateContractHash(sender, script).StringLE())
|
||||
require.Equal(t, "66eec404d86b918d084e62a29ac9990e3b6f4286", CreateContractHash(sender, neff.Checksum, "").StringLE())
|
||||
}
|
||||
|
||||
func TestContractFromStackItem(t *testing.T) {
|
||||
|
|
|
@ -56,11 +56,11 @@ type rpcTestCase struct {
|
|||
check func(t *testing.T, e *executor, result interface{})
|
||||
}
|
||||
|
||||
const testContractHash = "0b3bc97e94ed99e32dda46c9ecd2d3626979af06"
|
||||
const deploymentTxHash = "e3a67acac29014dc8c24773752ac4535a0f020486749ec5c907234fc9328246c"
|
||||
const testContractHash = "c6436aab21ebd15279b85af8d7b5808d38455b0a"
|
||||
const deploymentTxHash = "e6ffce4533231c4efdea9a65c7abc0e7073d96a4ebc66f402db3a84b6f8939ef"
|
||||
const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70"
|
||||
|
||||
const verifyContractHash = "d2da8ee8c0bf6c5bf3dda1ef671dbf5fef7226e9"
|
||||
const verifyContractHash = "03ffc0897543b9b709e0f8cab4a7682dae0ba943"
|
||||
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
|
||||
const testVerifyContractAVM = "VwcADBQBDAMOBQYMDQIODw0DDgcJAAAAANswcGgRVUH4J+yMIaonBwAAABFADBQNDwMCCQACAQMHAwQFAgEADgYMCdswcWkRVUH4J+yMIaonBwAAABJAE0A="
|
||||
|
||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
Loading…
Reference in a new issue