github.com/halybang/go-ethereum@v1.0.5-0.20180325041310-3b262bc1367c/core/gas_econ_test.go (about)

     1  // Copyright 2018 Wanchain Foundation Ltd
     2  
     3  package core
     4  
     5  import (
     6  	"crypto/ecdsa"
     7  	"errors"
     8  	"fmt"
     9  	"math/big"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/wanchain/go-wanchain/accounts/abi"
    14  	"github.com/wanchain/go-wanchain/accounts/keystore"
    15  	"github.com/wanchain/go-wanchain/common"
    16  	"github.com/wanchain/go-wanchain/common/hexutil"
    17  	"github.com/wanchain/go-wanchain/consensus/ethash"
    18  	"github.com/wanchain/go-wanchain/core/state"
    19  	"github.com/wanchain/go-wanchain/core/types"
    20  	"github.com/wanchain/go-wanchain/core/vm"
    21  	"github.com/wanchain/go-wanchain/crypto"
    22  	"github.com/wanchain/go-wanchain/ethdb"
    23  	"github.com/wanchain/go-wanchain/params"
    24  )
    25  
    26  const (
    27  	coinSCDefinition = `
    28  	[{"constant": false,"type": "function","stateMutability": "nonpayable","inputs": [{"name": "OtaAddr","type":"string"},{"name": "Value","type": "uint256"}],"name": "buyCoinNote","outputs": [{"name": "OtaAddr","type":"string"},{"name": "Value","type": "uint256"}]},{"constant": false,"type": "function","inputs": [{"name":"RingSignedData","type": "string"},{"name": "Value","type": "uint256"}],"name": "refundCoin","outputs": [{"name": "RingSignedData","type": "string"},{"name": "Value","type": "uint256"}]},{"constant": false,"type": "function","stateMutability": "nonpayable","inputs": [],"name": "getCoins","outputs": [{"name":"Value","type": "uint256"}]}]`
    29  )
    30  
    31  var (
    32  	wanCoinSCAddr = common.BytesToAddress([]byte{100})
    33  
    34  	otaBalanceStorageAddr = common.BytesToAddress(big.NewInt(300).Bytes())
    35  )
    36  
    37  var (
    38  	errOTAGen = errors.New("Fail to generate OTA")
    39  )
    40  
    41  func TestGasOrdinaryCoinTransfer(t *testing.T) {
    42  	var (
    43  		initialBalance = big.NewInt(1000000000)
    44  		// value of Wan coin to transfer
    45  		transferValue = big.NewInt(100000)
    46  		// gas price
    47  		gp = big.NewInt(100)
    48  		// gas used by the transaction
    49  		gasUsed   = new(big.Int)
    50  		db, _     = ethdb.NewMemDatabase()
    51  		engine    = ethash.NewFaker(db)
    52  		sk, _     = crypto.GenerateKey()
    53  		rk, _     = crypto.GenerateKey()
    54  		ck, _     = crypto.GenerateKey()
    55  		sender    = crypto.PubkeyToAddress(sk.PublicKey)
    56  		recipient = crypto.PubkeyToAddress(rk.PublicKey)
    57  		coinbase  = crypto.PubkeyToAddress(ck.PublicKey)
    58  	)
    59  
    60  	// initialize valid signers to write blocks
    61  	l := 5
    62  	extraData := append(make([]byte, 0), coinbase[:]...)
    63  	keySlice, addrSlice := make([]*ecdsa.PrivateKey, l), make([]common.Address, l)
    64  	for i := 0; i < l; i++ {
    65  		keySlice[i], _ = crypto.GenerateKey()
    66  		addrSlice[i] = crypto.PubkeyToAddress(keySlice[i].PublicKey)
    67  		extraData = append(extraData, addrSlice[i].Bytes()...)
    68  	}
    69  
    70  	// make the transaction
    71  	gspec := &Genesis{
    72  		Config:     params.TestChainConfig,
    73  		GasLimit:   0x47b760,
    74  		ExtraData:  extraData,
    75  		Difficulty: big.NewInt(1),
    76  		Alloc:      GenesisAlloc{sender: {Balance: initialBalance}},
    77  	}
    78  	genesis := gspec.MustCommit(db)
    79  
    80  	blockchain, _ := NewBlockChain(db, gspec.Config, engine, vm.Config{})
    81  	defer blockchain.Stop()
    82  
    83  	chainEnv := NewChainEnv(params.TestChainConfig, gspec, engine, blockchain, db)
    84  
    85  	signer := types.NewEIP155Signer(big.NewInt(gspec.Config.ChainId.Int64()))
    86  	chain, _ := chainEnv.GenerateChainMulti(genesis, 1, func(i int, gen *BlockGen) {
    87  		gen.SetCoinbase(coinbase)
    88  		tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(sender), recipient, transferValue, new(big.Int).SetUint64(params.TxGas), gp, nil), signer, sk)
    89  		gasUsed = gen.AddTxAndCalcGasUsed(tx)
    90  	})
    91  
    92  	if i, err := blockchain.InsertChain(chain); err != nil {
    93  		t.Fatalf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
    94  		return
    95  	}
    96  
    97  	// retrieve current state and account balances after the transaction
    98  	state, _ := blockchain.State()
    99  
   100  	senderBalance := state.GetBalance(sender)
   101  	recipientBalance := state.GetBalance(recipient)
   102  	coinbaseBalance := state.GetBalance(coinbase)
   103  
   104  	if blockchain.CurrentBlock().Number().Cmp(big.NewInt(1)) != 0 {
   105  		t.Fatal("fail to generate new block")
   106  	}
   107  
   108  	if coinbaseBalance.Cmp(new(big.Int).Mul(gp, gasUsed)) != 0 {
   109  		t.Fatal("coinbase rewards error")
   110  	}
   111  
   112  	if initialBalance.Cmp(new(big.Int).Add(senderBalance, new(big.Int).Add(recipientBalance, coinbaseBalance))) != 0 {
   113  		t.Fatal("wrong total balance")
   114  	}
   115  }
   116  
   117  func TestGasCoinMint(t *testing.T) {
   118  	var (
   119  		initialBalance = big.NewInt(0)
   120  		// value of Wan coin to transfer
   121  		transferValue = big.NewInt(0)
   122  		// gasLimit
   123  		gl = new(big.Int).SetUint64(params.SstoreSetGas * 20)
   124  		// gas price
   125  		gp = big.NewInt(100)
   126  		// gas used by the transaction
   127  		gasUsed  = new(big.Int)
   128  		db, _    = ethdb.NewMemDatabase()
   129  		engine   = ethash.NewFaker(db)
   130  		sk, _    = crypto.GenerateKey()
   131  		rkA, _   = crypto.GenerateKey()
   132  		rkB, _   = crypto.GenerateKey()
   133  		ck, _    = crypto.GenerateKey()
   134  		sender   = crypto.PubkeyToAddress(sk.PublicKey)
   135  		coinbase = crypto.PubkeyToAddress(ck.PublicKey)
   136  	)
   137  
   138  	initialBalance.SetString("20000000000000000000", 10)
   139  	transferValue.SetString("10000000000000000000", 10)
   140  
   141  	OTAStr, err := genOTAStr(&rkA.PublicKey, &rkB.PublicKey)
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	mintCoinData, err := genBuyCoinData(OTAStr, transferValue)
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	// initialize valid signers to write blocks
   152  	l := 5
   153  	extraData := append(make([]byte, 0), coinbase[:]...)
   154  	keySlice, addrSlice := make([]*ecdsa.PrivateKey, l), make([]common.Address, l)
   155  	for i := 0; i < l; i++ {
   156  		keySlice[i], _ = crypto.GenerateKey()
   157  		addrSlice[i] = crypto.PubkeyToAddress(keySlice[i].PublicKey)
   158  		extraData = append(extraData, addrSlice[i].Bytes()...)
   159  	}
   160  
   161  	// make the transaction
   162  	gspec := &Genesis{
   163  		Config:     params.TestChainConfig,
   164  		GasLimit:   0x47b760,
   165  		ExtraData:  extraData,
   166  		Difficulty: big.NewInt(1),
   167  		Alloc:      GenesisAlloc{sender: {Balance: initialBalance}},
   168  	}
   169  	genesis := gspec.MustCommit(db)
   170  	blockchain, _ := NewBlockChain(db, gspec.Config, engine, vm.Config{})
   171  	defer blockchain.Stop()
   172  
   173  	chainEnv := NewChainEnv(params.TestChainConfig, gspec, engine, blockchain, db)
   174  
   175  	signer := types.NewEIP155Signer(big.NewInt(gspec.Config.ChainId.Int64()))
   176  	chain, _ := chainEnv.GenerateChainMulti(genesis, 1, func(i int, gen *BlockGen) {
   177  		gen.SetCoinbase(coinbase)
   178  		tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(sender), wanCoinSCAddr, transferValue, gl, gp, mintCoinData), signer, sk)
   179  		gasUsed = gen.AddTxAndCalcGasUsed(tx)
   180  	})
   181  
   182  	if i, err := blockchain.InsertChain(chain); err != nil {
   183  		t.Fatalf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
   184  		return
   185  	}
   186  
   187  	// retrieve current state and account balances after the transaction
   188  	state, _ := blockchain.State()
   189  
   190  	senderBalance := state.GetBalance(sender)
   191  	OTABalance := getOTABalance(state, OTAStr)
   192  	coinbaseBalance := state.GetBalance(coinbase)
   193  
   194  	if blockchain.CurrentBlock().Number().Cmp(big.NewInt(1)) != 0 {
   195  		t.Fatal("fail to generate new block")
   196  	}
   197  
   198  	if coinbaseBalance.Cmp(new(big.Int).Mul(gp, gasUsed)) != 0 {
   199  		t.Fatal("coinbase rewards error")
   200  	}
   201  
   202  	if initialBalance.Cmp(new(big.Int).Add(senderBalance, new(big.Int).Add(OTABalance, coinbaseBalance))) != 0 {
   203  		t.Fatal("wrong total balance")
   204  	}
   205  
   206  }
   207  
   208  func TestGasCoinRefund(t *testing.T) {
   209  	var (
   210  		initialBalance = big.NewInt(0)
   211  		// value of Wan coin to transfer
   212  		transferValue = big.NewInt(0)
   213  		// gasLimit
   214  		gl = new(big.Int).SetUint64(params.SstoreSetGas * 20)
   215  		// gas price
   216  		gp = big.NewInt(100)
   217  		// gas used by the transaction
   218  		gasUsed   = new(big.Int)
   219  		gasUsedC1 = new(big.Int)
   220  		gasUsedC2 = new(big.Int)
   221  		// ota set elements count, which does not include the true OTA
   222  		setSize           = 2
   223  		db, _             = ethdb.NewMemDatabase()
   224  		engine            = ethash.NewFaker(db)
   225  		sk, _             = crypto.GenerateKey()
   226  		skContributor1, _ = crypto.GenerateKey()
   227  		skContributor2, _ = crypto.GenerateKey()
   228  		rkA, _            = crypto.GenerateKey()
   229  		rkB, _            = crypto.GenerateKey()
   230  		ck, _             = crypto.GenerateKey()
   231  		sender            = crypto.PubkeyToAddress(sk.PublicKey)
   232  		contributor1      = crypto.PubkeyToAddress(skContributor1.PublicKey)
   233  		contributor2      = crypto.PubkeyToAddress(skContributor2.PublicKey)
   234  		coinbase          = crypto.PubkeyToAddress(ck.PublicKey)
   235  	)
   236  
   237  	initialBalance.SetString("20000000000000000000", 10)
   238  	transferValue.SetString("10000000000000000000", 10)
   239  
   240  	// generate OTAs
   241  	OTAStr, err := genOTAStr(&rkA.PublicKey, &rkB.PublicKey)
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  
   246  	OTAC1, err := genOTAStr(&rkA.PublicKey, &rkB.PublicKey)
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  
   251  	OTAC2, err := genOTAStr(&rkA.PublicKey, &rkB.PublicKey)
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  
   256  	// generate ABI data
   257  	mintCoinData, err := genBuyCoinData(OTAStr, transferValue)
   258  	if err != nil {
   259  		t.Fatal(err)
   260  	}
   261  
   262  	mintCoinDataContributor1, err := genBuyCoinData(OTAC1, transferValue)
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  
   267  	mintCoinDataContributor2, err := genBuyCoinData(OTAC2, transferValue)
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  
   272  	// initialize valid signers to write blocks
   273  	l := 5
   274  	extraData := append(make([]byte, 0), coinbase[:]...)
   275  	keySlice, addrSlice := make([]*ecdsa.PrivateKey, l), make([]common.Address, l)
   276  	for i := 0; i < l; i++ {
   277  		keySlice[i], _ = crypto.GenerateKey()
   278  		addrSlice[i] = crypto.PubkeyToAddress(keySlice[i].PublicKey)
   279  		extraData = append(extraData, addrSlice[i].Bytes()...)
   280  	}
   281  
   282  	// make the transaction
   283  	gspec := &Genesis{
   284  		Config:     params.TestChainConfig,
   285  		GasLimit:   0x47b760,
   286  		ExtraData:  extraData,
   287  		Difficulty: big.NewInt(1),
   288  		Alloc: GenesisAlloc{
   289  			sender:       {Balance: initialBalance},
   290  			contributor1: {Balance: initialBalance},
   291  			contributor2: {Balance: initialBalance},
   292  		},
   293  	}
   294  	genesis := gspec.MustCommit(db)
   295  	blockchain, _ := NewBlockChain(db, gspec.Config, engine, vm.Config{})
   296  	defer blockchain.Stop()
   297  
   298  	chainEnv := NewChainEnv(params.TestChainConfig, gspec, engine, blockchain, db)
   299  
   300  	signer := types.NewEIP155Signer(big.NewInt(gspec.Config.ChainId.Int64()))
   301  	signerC1 := types.NewEIP155Signer(big.NewInt(gspec.Config.ChainId.Int64()))
   302  	signerC2 := types.NewEIP155Signer(big.NewInt(gspec.Config.ChainId.Int64()))
   303  	chain, _ := chainEnv.GenerateChainMulti(genesis, 1, func(i int, gen *BlockGen) {
   304  		// set coinbase
   305  		gen.SetCoinbase(coinbase)
   306  
   307  		// add transactions
   308  		tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(sender), wanCoinSCAddr, transferValue, gl, gp, mintCoinData), signer, sk)
   309  		txC1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(contributor1), wanCoinSCAddr, transferValue, gl, gp, mintCoinDataContributor1), signerC1, skContributor1)
   310  		txC2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(contributor2), wanCoinSCAddr, transferValue, gl, gp, mintCoinDataContributor2), signerC2, skContributor2)
   311  
   312  		// collect gas spent
   313  		gasUsed = gen.AddTxAndCalcGasUsed(tx)
   314  		gasUsedC1 = gen.AddTxAndCalcGasUsed(txC1)
   315  		gasUsedC2 = gen.AddTxAndCalcGasUsed(txC2)
   316  	})
   317  
   318  	if i, err := blockchain.InsertChain(chain); err != nil {
   319  		t.Fatalf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
   320  		return
   321  	}
   322  
   323  	// retrieve current state and account balances after the coin-mint transaction
   324  	state, _ := blockchain.State()
   325  
   326  	senderBalance := state.GetBalance(sender)
   327  	contributor1Balance := state.GetBalance(contributor1)
   328  	contributor2Balance := state.GetBalance(contributor2)
   329  	OTABalance := getOTABalance(state, OTAStr)
   330  	OTAC1Balance := getOTABalance(state, OTAC1)
   331  	OTAC2Balance := getOTABalance(state, OTAC2)
   332  	coinbaseBalance := state.GetBalance(coinbase)
   333  
   334  	if blockchain.CurrentBlock().Number().Cmp(big.NewInt(1)) != 0 {
   335  		t.Fatal("fail to generate a new block")
   336  	}
   337  
   338  	if coinbaseBalance.Cmp(new(big.Int).Add(new(big.Int).Mul(gp, gasUsed), new(big.Int).Add(new(big.Int).Mul(gp, gasUsedC1), new(big.Int).Mul(gp, gasUsedC2)))) != 0 {
   339  		t.Fatal("coinbase rewards error")
   340  	}
   341  
   342  	if new(big.Int).Add(new(big.Int).Add(new(big.Int).Add(new(big.Int).Add(new(big.Int).Add(new(big.Int).Add(senderBalance, OTAC1Balance), coinbaseBalance), contributor1Balance), contributor2Balance), OTABalance), OTAC2Balance).Cmp(new(big.Int).Mul(big.NewInt(3), initialBalance)) != 0 {
   343  		t.Fatal("Wrong total balance")
   344  	}
   345  
   346  	OTASet, err := genOTASet(state, OTAStr, setSize)
   347  	if err != nil {
   348  		t.Fatal(err)
   349  	}
   350  	for _, v := range OTASet {
   351  		fmt.Println(v)
   352  	}
   353  
   354  	keyPairs, err := computeOTAPubKeys(rkA, rkB, strings.Replace(OTAStr, "0x", "", -1))
   355  	if err != nil {
   356  		t.Fatal(err)
   357  	}
   358  	fmt.Println(keyPairs)
   359  }
   360  
   361  // generate recipient's OTA for privary transaction
   362  func genOTAStr(pk, pk1 *ecdsa.PublicKey) (string, error) {
   363  	PKPair := hexutil.PKPair2HexSlice(pk, pk1)
   364  	OTA, err := crypto.GenerateOneTimeKey(PKPair[0], PKPair[1], PKPair[2], PKPair[3])
   365  	if err != nil {
   366  		return "", errOTAGen
   367  	}
   368  
   369  	OTAStr := strings.Replace(strings.Join(OTA, ""), "0x", "", -1)
   370  	OTARaw, err := hexutil.Decode("0x" + OTAStr)
   371  	if err != nil {
   372  		return "", errOTAGen
   373  	}
   374  
   375  	OTAWanFormatRaw, err := keystore.WaddrFromUncompressedRawBytes(OTARaw)
   376  	if err != nil {
   377  		return "", errOTAGen
   378  	}
   379  
   380  	return hexutil.Encode(OTAWanFormatRaw[:]), nil
   381  }
   382  
   383  // generate data for wan coin mint transaction
   384  func genBuyCoinData(ota string, value *big.Int) ([]byte, error) {
   385  	coinABI, _ := abi.JSON(strings.NewReader(coinSCDefinition))
   386  	data, err := coinABI.Pack("buyCoinNote", ota, value)
   387  	return data, err
   388  }
   389  
   390  // retrieve OTA's balance from state trie
   391  func getOTABalance(db *state.StateDB, ota string) *big.Int {
   392  	otaAX, _ := vm.GetAXFromWanAddr(common.FromHex(ota))
   393  	balance := db.GetStateByteArray(otaBalanceStorageAddr, common.BytesToHash(otaAX))
   394  	return new(big.Int).SetBytes(balance)
   395  }
   396  
   397  // return OTA set with num elements
   398  func genOTASet(db *state.StateDB, ota string, num int) ([]string, error) {
   399  	otaAX, _ := vm.GetAXFromWanAddr(common.FromHex(ota))
   400  	otaSet, _, err := vm.GetOTASet(db, otaAX, num)
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  	ret := make([]string, 0)
   405  	for _, ota := range otaSet {
   406  		ret = append(ret, common.ToHex(ota))
   407  	}
   408  
   409  	return ret, err
   410  }
   411  
   412  func computeOTAPubKeys(pk, pk1 *ecdsa.PrivateKey, ota string) (string, error) {
   413  	strs := [4]string{}
   414  	l := 32
   415  	for i := 0; i < len(ota)/l; i++ {
   416  		strs[i] = "0x" + ota[i*l:(i+1)*l]
   417  	}
   418  
   419  	pub1, priv1, _, err := crypto.GenerteOTAPrivateKey(pk, pk1, strs[0], strs[1], strs[2], strs[3])
   420  	if err != nil {
   421  		return "", err
   422  	}
   423  
   424  	pub1X := hexutil.Encode(common.LeftPadBytes(pub1.X.Bytes(), 32))
   425  	pub1Y := hexutil.Encode(common.LeftPadBytes(pub1.Y.Bytes(), 32))
   426  	priv1D := hexutil.Encode(common.LeftPadBytes(priv1.D.Bytes(), 32))
   427  
   428  	otaPub := pub1X + pub1Y[2:]
   429  	otaPriv := priv1D
   430  
   431  	sk, err := crypto.HexToECDSA(otaPriv[2:])
   432  	if err != nil {
   433  		return "", err
   434  	}
   435  
   436  	var addr common.Address
   437  	pubkey := crypto.FromECDSAPub(&sk.PublicKey)
   438  	copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
   439  
   440  	return otaPriv + "+" + otaPub + "+" + hexutil.Encode(addr[:]), err
   441  }
   442  
   443  func genRingSignData(addr common.Address) {
   444  
   445  }