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 }