diff --git a/account/wallet_test.go b/account/wallet_test.go index 4eb40a8..9d7ec27 100644 --- a/account/wallet_test.go +++ b/account/wallet_test.go @@ -18,15 +18,16 @@ package account import ( "fmt" + "os" + "runtime" + "strconv" + "testing" + "github.com/Zilliqa/gozilliqa-sdk/core" provider2 "github.com/Zilliqa/gozilliqa-sdk/provider" "github.com/Zilliqa/gozilliqa-sdk/transaction" "github.com/Zilliqa/gozilliqa-sdk/util" "github.com/stretchr/testify/assert" - "os" - "runtime" - "strconv" - "testing" ) func TestPayload(t *testing.T) { @@ -172,7 +173,7 @@ func TestSendTransactionInsufficientAmount(t *testing.T) { Version: strconv.FormatInt(int64(util.Pack(333, 1)), 10), SenderPubKey: "0246E7178DC8253201101E18FD6F6EB9972451D121FC57AA2A06DD5C111E58DC6A", ToAddr: "4BAF5faDA8e5Db92C3d3242618c5B47133AE003C", - Amount: "10000000000000000", + Amount: "2000000000000000000", GasPrice: gasPrice, GasLimit: "1", Code: "", diff --git a/contract/contract.go b/contract/contract.go index 1b330fb..999c722 100644 --- a/contract/contract.go +++ b/contract/contract.go @@ -22,11 +22,20 @@ import ( "github.com/Zilliqa/gozilliqa-sdk/core" "github.com/Zilliqa/gozilliqa-sdk/provider" "github.com/Zilliqa/gozilliqa-sdk/transaction" + "github.com/Zilliqa/gozilliqa-sdk/util" + "strconv" "strings" ) type ContractStatus int +const MainNet = "mainnet" +const TestNet = "testnet" +const Isolated = "isolated" +const TestNetHost = "https://dev-api.zilliqa.com/" +const MainNetHost = "https://api.zilliqa.com/" +const IsolatedHost = "https://zilliqa-isolated-server.zilliqa.com/" + const ( Deployed ContractStatus = iota Rejected @@ -49,6 +58,58 @@ type State struct { core.ContractValue } +// This a shortcut function to deploy smart contract, all following parameters would remain default: +// network id, message version nonce, gasfee, gaslimit +// take a note that for gas limit, 40k is safe and recommend setting number +// value of network can be testnet, mainnet or isolated +func (c *Contract) DeployTo(network string) (*transaction.Transaction, error) { + if network == TestNet { + c.Provider = provider.NewProvider(TestNetHost) + gasPrice, err := c.Provider.GetMinimumGasPrice() + if err != nil { + return nil, err + } + parameter := DeployParams{ + Version: strconv.FormatInt(int64(util.Pack(333, 1)), 10), + Nonce: "", + GasPrice: gasPrice, + GasLimit: "40000", + SenderPubKey: "", + } + return c.Deploy(parameter) + } else if network == MainNet { + c.Provider = provider.NewProvider(MainNetHost) + gasPrice, err := c.Provider.GetMinimumGasPrice() + if err != nil { + return nil, err + } + parameter := DeployParams{ + Version: strconv.FormatInt(int64(util.Pack(1, 1)), 10), + Nonce: "", + GasPrice: gasPrice, + GasLimit: "40000", + SenderPubKey: "", + } + return c.Deploy(parameter) + } else if network == Isolated { + c.Provider = provider.NewProvider(IsolatedHost) + gasPrice, err := c.Provider.GetMinimumGasPrice() + if err != nil { + return nil, err + } + parameter := DeployParams{ + Version: strconv.FormatInt(int64(util.Pack(1, 1)), 10), + Nonce: "", + GasPrice: gasPrice, + GasLimit: "40000", + SenderPubKey: "", + } + return c.Deploy(parameter) + } else { + return nil, errors.New("unsupported network, please use testnet, mainnet or isolated") + } +} + func (c *Contract) Deploy(params DeployParams) (*transaction.Transaction, error) { if c.Code == "" || c.Init == nil || len(c.Init) == 0 { return nil, errors.New("Cannot deploy without code or initialisation parameters.") @@ -133,6 +194,58 @@ func (c *Contract) Sign(transition string, args []core.ContractValue, params Cal return nil, tx } +func (c *Contract) CallFor(transition string, args []core.ContractValue, priority bool, amount string, network string) (*transaction.Transaction, error) { + if network == TestNet { + c.Provider = provider.NewProvider(TestNetHost) + gasPrice, err := c.Provider.GetMinimumGasPrice() + if err != nil { + return nil, err + } + params := CallParams{ + Version: strconv.FormatInt(int64(util.Pack(333, 1)), 10), + Nonce: "", + GasPrice: gasPrice, + GasLimit: "40000", + Amount: amount, + SenderPubKey: "", + } + return c.Call(transition, args, params, priority) + } else if network == MainNet { + c.Provider = provider.NewProvider(MainNetHost) + gasPrice, err := c.Provider.GetMinimumGasPrice() + if err != nil { + return nil, err + } + params := CallParams{ + Version: strconv.FormatInt(int64(util.Pack(1, 1)), 10), + Nonce: "", + GasPrice: gasPrice, + GasLimit: "40000", + Amount: amount, + SenderPubKey: "", + } + return c.Call(transition, args, params, priority) + } else if network == Isolated { + c.Provider = provider.NewProvider(IsolatedHost) + gasPrice, err := c.Provider.GetMinimumGasPrice() + if err != nil { + return nil, err + } + params := CallParams{ + Version: strconv.FormatInt(int64(util.Pack(1, 1)), 10), + Nonce: "", + GasPrice: gasPrice, + GasLimit: "40000", + Amount: amount, + SenderPubKey: "", + } + return c.Call(transition, args, params, priority) + } else { + return nil, errors.New("unsupported network, please use testnet, mainnet or isolated") + } + +} + func (c *Contract) Call(transition string, args []core.ContractValue, params CallParams, priority bool) (*transaction.Transaction, error) { if c.Address == "" { _ = errors.New("Contract has not been deployed!") diff --git a/contract/contract_test.go b/contract/contract_test.go index 40f83c4..d349015 100644 --- a/contract/contract_test.go +++ b/contract/contract_test.go @@ -17,6 +17,7 @@ package contract import ( + "fmt" "github.com/Zilliqa/gozilliqa-sdk/core" "github.com/stretchr/testify/assert" "io/ioutil" @@ -30,6 +31,63 @@ import ( "github.com/Zilliqa/gozilliqa-sdk/util" ) +func TestContract_DeployTo(t *testing.T) { + if os.Getenv("CI") != "" { + t.Skip("Skipping testing in CI environment") + } + + privateKey := "e19d05c5452598e24caad4a0d85a49146f7be089515c905ae6a19e8a578a6930" + wallet := account.NewWallet() + wallet.AddByPrivateKey(privateKey) + + publickKey := keytools.GetPublicKeyFromPrivateKey(util.DecodeHex(privateKey), true) + address := keytools.GetAddressFromPublic(publickKey) + code, _ := ioutil.ReadFile("./fungible.scilla") + init := []core.ContractValue{ + { + "_scilla_version", + "Uint32", + "0", + }, + { + "owner", + "ByStr20", + "0x" + address, + }, + { + "total_tokens", + "Uint128", + "1000000000", + }, + { + "decimals", + "Uint32", + "0", + }, + { + "name", + "String", + "BobCoin", + }, + { + "symbol", + "String", + "BOB", + }, + } + + contract := Contract{ + Code: string(code), + Init: init, + Signer: wallet, + } + + tx, err := contract.DeployTo(TestNet) + assert.Nil(t, err, err) + tx.Confirm(tx.ID, 1000, 10, contract.Provider) + assert.True(t, tx.Status == core.Confirmed) +} + func TestContract_Deploy(t *testing.T) { if os.Getenv("CI") != "" { t.Skip("Skipping testing in CI environment") @@ -105,6 +163,43 @@ func TestContract_Deploy(t *testing.T) { assert.True(t, tx.Status == core.Confirmed) } +func TestContract_CallFor(t *testing.T) { + if os.Getenv("CI") != "" { + t.Skip("Skipping testing in CI environment") + } + + privateKey := "e19d05c5452598e24caad4a0d85a49146f7be089515c905ae6a19e8a578a6930" + wallet := account.NewWallet() + wallet.AddByPrivateKey(privateKey) + publickKey := keytools.GetPublicKeyFromPrivateKey(util.DecodeHex(privateKey), true) + address := keytools.GetAddressFromPublic(publickKey) + fmt.Println(address) + + contract := Contract{ + Address: "bd7198209529dC42320db4bC8508880BcD22a9f2", + Signer: wallet, + } + + args := []core.ContractValue{ + { + "to", + "ByStr20", + "0x" + address, + }, + { + "tokens", + "Uint128", + "10", + }, + } + + tx, err2 := contract.CallFor("Transfer", args, true, "0", TestNet) + assert.Nil(t, err2, err2) + tx.Confirm(tx.ID, 1000, 3, contract.Provider) + assert.True(t, tx.Status == core.Confirmed) + +} + func TestContract_Call(t *testing.T) { if os.Getenv("CI") != "" { t.Skip("Skipping testing in CI environment") diff --git a/core/types.go b/core/types.go index d46692e..9cc9e85 100644 --- a/core/types.go +++ b/core/types.go @@ -156,6 +156,12 @@ type ContractValue struct { Value interface{} `json:"value"` } +type ParamConstructor struct { + Constructor string `json:"constructor"` + ArgTypes []interface{} `json:"argtypes"` + Arguments []string `json:"arguments"` +} + type BalanceAndNonce struct { Balance string `json:"balance"` Nonce int64 `json:"nonce"` diff --git a/schnorr/schnorr_signer_wrapper.go b/schnorr/schnorr_signer_wrapper.go new file mode 100644 index 0000000..b2e7b03 --- /dev/null +++ b/schnorr/schnorr_signer_wrapper.go @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 Zilliqa + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This module is a mini wrapper around the main schnorr module used for + * zilliqa rosetta project. It includes the generation of the random bytes + * to make the signing process more convenient. + */ +package go_schnorr + +import ( + "encoding/hex" + "fmt" + + "github.com/Zilliqa/gozilliqa-sdk/keytools" + "github.com/Zilliqa/gozilliqa-sdk/util" +) + +func SignMessage(privateKey []byte, publicKey []byte, message []byte) ([]byte, error) { + // generate a random byte k + rb, err := keytools.GenerateRandomBytes(keytools.Secp256k1.N.BitLen() / 8) + + if err != nil { + return nil, err + } + + r, s, err2 := TrySign(privateKey, publicKey, message, rb) + + if err2 != nil { + return nil, err2 + } + + sig := fmt.Sprintf("%064s%064s", util.EncodeHex(r), util.EncodeHex(s)) + sigBytes, err := hex.DecodeString(sig) + + if err != nil { + panic("cannot convert hex string to byte array") + } + + return sigBytes, nil +} + +func VerifySignature(publicKey []byte, message []byte, signature []byte) bool { + sig := util.EncodeHex(signature) + + if len(sig) != 128 { + fmt.Printf("invalid signature length: %v\n", len(sig)) + return false + } + + r := util.DecodeHex(sig[0:64]) + s := util.DecodeHex(sig[64:128]) + + return Verify(publicKey, message, r, s) +} diff --git a/schnorr/schnorr_signer_wrapper_test.go b/schnorr/schnorr_signer_wrapper_test.go new file mode 100644 index 0000000..d5ef01c --- /dev/null +++ b/schnorr/schnorr_signer_wrapper_test.go @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Zilliqa + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package go_schnorr + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "testing" + + golangAssert "github.com/stretchr/testify/assert" +) + +func TestSignVerify(t *testing.T) { + run_sign_verify_test(t) +} + +func run_sign_verify_test(t *testing.T) { + b, err := ioutil.ReadFile("data") + if err != nil { + panic("read file failed") + } + + var data []map[string]string + err2 := json.Unmarshal(b, &data) + + if err2 != nil { + panic("unmarshal failed") + } + + for _, v := range data { + msg := hex_bytes(v["msg"]) + pub := hex_bytes(v["pub"]) + priv := hex_bytes(v["priv"]) + + sig, err := SignMessage(priv, pub, msg) + + if err != nil { + fmt.Printf("err = %s\n", err.Error()) + } else { + verify := VerifySignature(pub, msg, sig) + + fmt.Printf("signature = %v\n", sig) + fmt.Printf("expected verify = %v\n", true) + fmt.Printf("actually verify = %v\n", verify) + golangAssert.True(t, verify, "verify should be true") + } + } +}