github.com/zjj1991/quorum@v0.0.0-20190524123704-ae4b0a1e1a19/core/private_state_test.go (about)

     1  package core
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"html/template"
     8  	"io/ioutil"
     9  	"math/big"
    10  	"net"
    11  	"net/http"
    12  	"os"
    13  	osExec "os/exec"
    14  	"path"
    15  	"path/filepath"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/ethereum/go-ethereum/common"
    21  	"github.com/ethereum/go-ethereum/crypto"
    22  	"github.com/ethereum/go-ethereum/private"
    23  	"github.com/ethereum/go-ethereum/private/constellation"
    24  )
    25  
    26  // callmsg is the message type used for call transactions in the private state test
    27  type callmsg struct {
    28  	addr     common.Address
    29  	to       *common.Address
    30  	gas      uint64
    31  	gasPrice *big.Int
    32  	value    *big.Int
    33  	data     []byte
    34  }
    35  
    36  // accessor boilerplate to implement core.Message
    37  func (m callmsg) From() common.Address         { return m.addr }
    38  func (m callmsg) FromFrontier() common.Address { return m.addr }
    39  func (m callmsg) Nonce() uint64                { return 0 }
    40  func (m callmsg) To() *common.Address          { return m.to }
    41  func (m callmsg) GasPrice() *big.Int           { return m.gasPrice }
    42  func (m callmsg) Gas() uint64                  { return m.gas }
    43  func (m callmsg) Value() *big.Int              { return m.value }
    44  func (m callmsg) Data() []byte                 { return m.data }
    45  func (m callmsg) CheckNonce() bool             { return true }
    46  
    47  func ExampleMakeCallHelper() {
    48  	var (
    49  		// setup new pair of keys for the calls
    50  		key, _ = crypto.GenerateKey()
    51  		// create a new helper
    52  		helper = MakeCallHelper()
    53  	)
    54  	// Private contract address
    55  	prvContractAddr := common.Address{1}
    56  	// Initialise custom code for private contract
    57  	helper.PrivateState.SetCode(prvContractAddr, common.Hex2Bytes("600a60005500"))
    58  	// Public contract address
    59  	pubContractAddr := common.Address{2}
    60  	// Initialise custom code for public contract
    61  	helper.PublicState.SetCode(pubContractAddr, common.Hex2Bytes("601460005500"))
    62  
    63  	// Make a call to the private contract
    64  	err := helper.MakeCall(true, key, prvContractAddr, nil)
    65  	if err != nil {
    66  		fmt.Println(err)
    67  	}
    68  	// Make a call to the public contract
    69  	err = helper.MakeCall(false, key, pubContractAddr, nil)
    70  	if err != nil {
    71  		fmt.Println(err)
    72  	}
    73  
    74  	// Output:
    75  	// Private: 10
    76  	// Public: 20
    77  	fmt.Println("Private:", helper.PrivateState.GetState(prvContractAddr, common.Hash{}).Big())
    78  	fmt.Println("Public:", helper.PublicState.GetState(pubContractAddr, common.Hash{}).Big())
    79  }
    80  
    81  var constellationCfgTemplate = template.Must(template.New("t").Parse(`
    82  	url = "http://127.0.0.1:9000/"
    83  	port = 9000
    84  	socketPath = "{{.RootDir}}/qdata/tm1.ipc"
    85  	otherNodeUrls = []
    86  	publicKeyPath = "{{.RootDir}}/keys/tm1.pub"
    87  	privateKeyPath = "{{.RootDir}}/keys/tm1.key"
    88  	archivalPublicKeyPath = "{{.RootDir}}/keys/tm1a.pub"
    89  	archivalPrivateKeyPath = "{{.RootDir}}/keys/tm1a.key"
    90  	storagePath = "{{.RootDir}}/qdata/constellation1"
    91  `))
    92  
    93  func runConstellation() (*osExec.Cmd, error) {
    94  	dir, err := ioutil.TempDir("", "TestPrivateTxConstellationData")
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	defer os.RemoveAll(dir)
    99  	here, err := os.Getwd()
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	if err = os.MkdirAll(path.Join(dir, "qdata"), 0755); err != nil {
   104  		return nil, err
   105  	}
   106  	if err = os.Symlink(path.Join(here, "constellation-test-keys"), path.Join(dir, "keys")); err != nil {
   107  		return nil, err
   108  	}
   109  	cfgFile, err := os.Create(path.Join(dir, "constellation.cfg"))
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	err = constellationCfgTemplate.Execute(cfgFile, map[string]string{"RootDir": dir})
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	constellationCmd := osExec.Command("constellation-node", cfgFile.Name())
   118  	var stdout, stderr bytes.Buffer
   119  	constellationCmd.Stdout = &stdout
   120  	constellationCmd.Stderr = &stderr
   121  	var constellationErr error
   122  	go func() {
   123  		constellationErr = constellationCmd.Start()
   124  	}()
   125  	// Give the constellation subprocess some time to start.
   126  	time.Sleep(5 * time.Second)
   127  	fmt.Println(stdout.String() + stderr.String())
   128  	if constellationErr != nil {
   129  		return nil, constellationErr
   130  	}
   131  	private.P = constellation.MustNew(cfgFile.Name())
   132  	return constellationCmd, nil
   133  }
   134  
   135  func runTessera() (*osExec.Cmd, error) {
   136  	tesseraVersion := "0.6"
   137  	// make sure JRE is available
   138  	if err := osExec.Command("java").Start(); err != nil {
   139  		return nil, fmt.Errorf("runTessera: java not available - %s", err.Error())
   140  	}
   141  	// download binary from github/release
   142  	dir, err := ioutil.TempDir("", "tessera")
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	defer os.RemoveAll(dir)
   147  	resp, err := http.Get(fmt.Sprintf("https://github.com/jpmorganchase/tessera/releases/download/tessera-%s/tessera-app-%s-app.jar", tesseraVersion, tesseraVersion))
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  	defer resp.Body.Close()
   152  	data, err := ioutil.ReadAll(resp.Body)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  	tesseraJar := filepath.Join(dir, "tessera.jar")
   157  	if err := ioutil.WriteFile(tesseraJar, data, os.FileMode(0644)); err != nil {
   158  		return nil, err
   159  	}
   160  	// create config.json file
   161  	here, err := os.Getwd()
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	if err = os.MkdirAll(path.Join(dir, "qdata"), 0755); err != nil {
   166  		return nil, err
   167  	}
   168  	tmIPCFile := filepath.Join(dir, "qdata", "tm.ipc")
   169  	keyData, err := ioutil.ReadFile(filepath.Join(here, "constellation-test-keys", "tm1.key"))
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	publicKeyData, err := ioutil.ReadFile(filepath.Join(here, "constellation-test-keys", "tm1.pub"))
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	tesseraConfigFile := filepath.Join(dir, "config.json")
   178  	if err := ioutil.WriteFile(tesseraConfigFile, []byte(fmt.Sprintf(`
   179  {
   180      "useWhiteList": false,
   181      "jdbc": {
   182          "username": "sa",
   183          "password": "",
   184          "url": "jdbc:h2:./qdata/c0/db0;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0"
   185      },
   186      "server": {
   187          "port": 9000,
   188          "hostName": "http://localhost",
   189          "sslConfig": {
   190              "tls": "OFF",
   191              "generateKeyStoreIfNotExisted": true,
   192              "serverKeyStore": "./qdata/c1/server1-keystore",
   193              "serverKeyStorePassword": "quorum",
   194              "serverTrustStore": "./qdata/c1/server-truststore",
   195              "serverTrustStorePassword": "quorum",
   196              "serverTrustMode": "TOFU",
   197              "knownClientsFile": "./qdata/c1/knownClients",
   198              "clientKeyStore": "./c1/client1-keystore",
   199              "clientKeyStorePassword": "quorum",
   200              "clientTrustStore": "./c1/client-truststore",
   201              "clientTrustStorePassword": "quorum",
   202              "clientTrustMode": "TOFU",
   203              "knownServersFile": "./qdata/c1/knownServers"
   204          }
   205      },
   206      "peer": [
   207          {
   208              "url": "http://localhost:9000"
   209          }
   210      ],
   211      "keys": {
   212          "passwords": [],
   213          "keyData": [
   214              {
   215                  "config": %s,
   216                  "publicKey": "%s"
   217              }
   218          ]
   219      },
   220      "alwaysSendTo": [],
   221      "unixSocketFile": "%s"
   222  }
   223  `, string(keyData), string(publicKeyData), tmIPCFile)), os.FileMode(0644)); err != nil {
   224  		return nil, err
   225  	}
   226  
   227  	cmdStatusChan := make(chan error)
   228  	cmd := osExec.Command("java", "-Xms128M", "-Xmx128M", "-jar", tesseraJar, "-configFile", tesseraConfigFile)
   229  	// run tessera
   230  	go func() {
   231  		err := cmd.Start()
   232  		cmdStatusChan <- err
   233  	}()
   234  	// wait for tessera to come up
   235  	go func() {
   236  		waitingErr := errors.New("waiting")
   237  		checkFunc := func() error {
   238  			conn, err := net.Dial("unix", tmIPCFile)
   239  			if err != nil {
   240  				return waitingErr
   241  			}
   242  			if _, err := conn.Write([]byte("GET /upcheck HTTP/1.0\r\n\r\n")); err != nil {
   243  				return waitingErr
   244  			}
   245  			result, err := ioutil.ReadAll(conn)
   246  			if err != nil || string(result) != "I'm up!" {
   247  				return waitingErr
   248  			}
   249  			return nil
   250  		}
   251  		for {
   252  			time.Sleep(3 * time.Second)
   253  			if err := checkFunc(); err != nil && err != waitingErr {
   254  				cmdStatusChan <- err
   255  			}
   256  		}
   257  	}()
   258  	if err := <-cmdStatusChan; err != nil {
   259  		return nil, err
   260  	}
   261  	// wait until tessera is up
   262  	return cmd, nil
   263  }
   264  
   265  // 600a600055600060006001a1
   266  // [1] PUSH1 0x0a (store value)
   267  // [3] PUSH1 0x00 (store addr)
   268  // [4] SSTORE
   269  // [6] PUSH1 0x00
   270  // [8] PUSH1 0x00
   271  // [10] PUSH1 0x01
   272  // [11] LOG1
   273  //
   274  // Store then log
   275  func TestPrivateTransaction(t *testing.T) {
   276  	var (
   277  		key, _       = crypto.GenerateKey()
   278  		helper       = MakeCallHelper()
   279  		privateState = helper.PrivateState
   280  		publicState  = helper.PublicState
   281  	)
   282  
   283  	constellationCmd, err := runConstellation()
   284  	if err != nil {
   285  		if strings.Contains(err.Error(), "executable file not found") {
   286  			if constellationCmd, err = runTessera(); err != nil {
   287  				t.Fatal(err)
   288  			}
   289  		} else {
   290  			t.Fatal(err)
   291  		}
   292  	}
   293  	defer constellationCmd.Process.Kill()
   294  
   295  	prvContractAddr := common.Address{1}
   296  	pubContractAddr := common.Address{2}
   297  	privateState.SetCode(prvContractAddr, common.Hex2Bytes("600a600055600060006001a1"))
   298  	privateState.SetState(prvContractAddr, common.Hash{}, common.Hash{9})
   299  	publicState.SetCode(pubContractAddr, common.Hex2Bytes("6014600055"))
   300  	publicState.SetState(pubContractAddr, common.Hash{}, common.Hash{19})
   301  
   302  	if publicState.Exist(prvContractAddr) {
   303  		t.Error("didn't expect private contract address to exist on public state")
   304  	}
   305  
   306  	// Private transaction 1
   307  	err = helper.MakeCall(true, key, prvContractAddr, nil)
   308  	if err != nil {
   309  		t.Fatal(err)
   310  	}
   311  	stateEntry := privateState.GetState(prvContractAddr, common.Hash{}).Big()
   312  	if stateEntry.Cmp(big.NewInt(10)) != 0 {
   313  		t.Error("expected state to have 10, got", stateEntry)
   314  	}
   315  	if len(privateState.Logs()) != 1 {
   316  		t.Error("expected private state to have 1 log, got", len(privateState.Logs()))
   317  	}
   318  	if len(publicState.Logs()) != 0 {
   319  		t.Error("expected public state to have 0 logs, got", len(publicState.Logs()))
   320  	}
   321  	if publicState.Exist(prvContractAddr) {
   322  		t.Error("didn't expect private contract address to exist on public state")
   323  	}
   324  	if !privateState.Exist(prvContractAddr) {
   325  		t.Error("expected private contract address to exist on private state")
   326  	}
   327  
   328  	// Public transaction 1
   329  	err = helper.MakeCall(false, key, pubContractAddr, nil)
   330  	if err != nil {
   331  		t.Fatal(err)
   332  	}
   333  	stateEntry = publicState.GetState(pubContractAddr, common.Hash{}).Big()
   334  	if stateEntry.Cmp(big.NewInt(20)) != 0 {
   335  		t.Error("expected state to have 20, got", stateEntry)
   336  	}
   337  
   338  	// Private transaction 2
   339  	err = helper.MakeCall(true, key, prvContractAddr, nil)
   340  	stateEntry = privateState.GetState(prvContractAddr, common.Hash{}).Big()
   341  	if stateEntry.Cmp(big.NewInt(10)) != 0 {
   342  		t.Error("expected state to have 10, got", stateEntry)
   343  	}
   344  
   345  	if publicState.Exist(prvContractAddr) {
   346  		t.Error("didn't expect private contract address to exist on public state")
   347  	}
   348  	if privateState.Exist(pubContractAddr) {
   349  		t.Error("didn't expect public contract address to exist on private state")
   350  	}
   351  }