From cc322429627c02c5c4b66b8115752e0604acb707 Mon Sep 17 00:00:00 2001 From: Minh Doan Date: Tue, 30 Apr 2019 00:19:43 -0700 Subject: [PATCH] add puzzle contract smart contract integrate smart contract add payout call and fix others fix play contract call --- api/service/restclientsupport/service.go | 92 ++++++-- cmd/restclientsupport/main.go | 2 +- contracts/Puzzle.go | 268 +++++++++++++++++++++++ contracts/Puzzle.sol | 37 ++++ contracts/gen.sh | 3 +- internal/utils/contract/constants.go | 16 ++ node/contract.go | 5 + node/demo_contract.go | 2 +- node/{node_error.go => errors.go} | 2 + node/node.go | 6 + node/puzzle_contract.go | 146 ++++++++++++ node/service_setup.go | 4 +- 12 files changed, 565 insertions(+), 18 deletions(-) create mode 100644 contracts/Puzzle.go create mode 100644 contracts/Puzzle.sol rename node/{node_error.go => errors.go} (53%) create mode 100644 node/puzzle_contract.go diff --git a/api/service/restclientsupport/service.go b/api/service/restclientsupport/service.go index b7fa5c2ea..ba1fb9aae 100644 --- a/api/service/restclientsupport/service.go +++ b/api/service/restclientsupport/service.go @@ -27,14 +27,16 @@ const ( // Service is the struct for rest client support service. type Service struct { - router *mux.Router - server *http.Server - CreateTransactionForEnterMethod func(int64, string) error - GetResult func(string) ([]string, []*big.Int) - CreateTransactionForPickWinner func() error - messageChan chan *msg_pb.Message - CallFaucetContract func(common.Address) common.Hash - GetAccountBalance func(common.Address) (*big.Int, error) + router *mux.Router + server *http.Server + CreateTransactionForEnterMethod func(int64, string) error + GetResult func(string) ([]string, []*big.Int) + CreateTransactionForPickWinner func() error + messageChan chan *msg_pb.Message + CallFaucetContract func(common.Address) common.Hash + GetAccountBalance func(common.Address) (*big.Int, error) + CreateTransactionForPlayMethod func(string) error + CreateTransactionForPayoutMethod func(string, int) error } // New returns new client support service. @@ -42,13 +44,17 @@ func New( CreateTransactionForEnterMethod func(int64, string) error, GetResult func(string) ([]string, []*big.Int), CreateTransactionForPickWinner func() error, - CallFaucetContract func(common.Address) common.Hash, GetAccountBalance func(common.Address) (*big.Int, error)) *Service { + CallFaucetContract func(common.Address) common.Hash, GetAccountBalance func(common.Address) (*big.Int, error), + CreateTransactionForPlayMethod func(string) error, + CreateTransactionForPayoutMethod func(string, int) error) *Service { return &Service{ - CreateTransactionForEnterMethod: CreateTransactionForEnterMethod, - GetResult: GetResult, - CreateTransactionForPickWinner: CreateTransactionForPickWinner, - CallFaucetContract: CallFaucetContract, - GetAccountBalance: GetAccountBalance, + CreateTransactionForEnterMethod: CreateTransactionForEnterMethod, + GetResult: GetResult, + CreateTransactionForPickWinner: CreateTransactionForPickWinner, + CallFaucetContract: CallFaucetContract, + GetAccountBalance: GetAccountBalance, + CreateTransactionForPlayMethod: CreateTransactionForPlayMethod, + CreateTransactionForPayoutMethod: CreateTransactionForPayoutMethod, } } @@ -93,6 +99,12 @@ func (s *Service) Run() *http.Server { // Set up router for winner. s.router.Path("/winner").HandlerFunc(s.Winner) + // Routing for puzzle app. + // Set up router for play. + s.router.Path("/play").HandlerFunc(s.Play) + + // Set up router for payout. + s.router.Path("/payout").HandlerFunc(s.Payout) // Do serving now. utils.GetLogInstance().Info("Listening on ", "port: ", Port) server := &http.Server{Addr: addr, Handler: s.router} @@ -226,6 +238,58 @@ func (s *Service) Winner(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(res) } +// Play triggers play method of puzzle smart contract. +func (s *Service) Play(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + key := r.FormValue("key") + + fmt.Println("puzzle-play", key) + res := &Response{ + Success: false, + } + + if s.CreateTransactionForPlayMethod == nil { + fmt.Println("puzzle-play no method", key) + json.NewEncoder(w).Encode(res) + return + } + + if err := s.CreateTransactionForPlayMethod(key); err != nil { + utils.GetLogInstance().Error("puzzle-play, error", err) + json.NewEncoder(w).Encode(res) + return + } + res.Success = true + json.NewEncoder(w).Encode(res) +} + +// Payout triggers play payout of puzzle smart contract. +func (s *Service) Payout(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + key := r.FormValue("key") + newLevel := r.FormValue("new_level") + fmt.Println("payout: key", key, "new_level", newLevel) + newLevelInt, err := strconv.Atoi(newLevel) + + fmt.Println("play") + res := &Response{ + Success: false, + } + + if s.CreateTransactionForPayoutMethod == nil { + json.NewEncoder(w).Encode(res) + return + } + + if err = s.CreateTransactionForPayoutMethod(key, newLevelInt); err != nil { + utils.GetLogInstance().Error("error", err) + json.NewEncoder(w).Encode(res) + return + } + res.Success = true + json.NewEncoder(w).Encode(res) +} + // NotifyService notify service func (s *Service) NotifyService(params map[string]interface{}) { return diff --git a/cmd/restclientsupport/main.go b/cmd/restclientsupport/main.go index 3800bf791..e81e1453d 100644 --- a/cmd/restclientsupport/main.go +++ b/cmd/restclientsupport/main.go @@ -7,7 +7,7 @@ import ( ) func main() { - s := restclientsupport.New(nil, nil, nil, nil, nil) + s := restclientsupport.New(nil, nil, nil, nil, nil, nil, nil) s.StartService() fmt.Println("Server started") select {} diff --git a/contracts/Puzzle.go b/contracts/Puzzle.go new file mode 100644 index 000000000..5fb02cb88 --- /dev/null +++ b/contracts/Puzzle.go @@ -0,0 +1,268 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +import ( + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// PuzzleABI is the input ABI used to generate the binding from. +const PuzzleABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"level_map\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"player\",\"type\":\"address\"},{\"name\":\"new_level\",\"type\":\"uint8\"}],\"name\":\"payout\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"manager\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[],\"name\":\"play\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"constructor\"}]" + +// PuzzleBin is the compiled bytecode used for deploying new contracts. +const PuzzleBin = `0x608060405260008054600160a060020a031916331790556103be806100256000396000f3fe60806040526004361061005b577c0100000000000000000000000000000000000000000000000000000000600035046311b88fef8114610060578063158b4aa6146100a9578063481c6a75146100da57806393e84cd91461010b575b600080fd5b34801561006c57600080fd5b506100936004803603602081101561008357600080fd5b5035600160a060020a0316610113565b6040805160ff9092168252519081900360200190f35b6100d8600480360360408110156100bf57600080fd5b508035600160a060020a0316906020013560ff16610128565b005b3480156100e657600080fd5b506100ef6102da565b60408051600160a060020a039092168252519081900360200190f35b6100d86102e9565b60016020526000908152604090205460ff1681565b60005460408051808201909152601381527f556e617574686f72697a65642041636365737300000000000000000000000000602082015290600160a060020a0316331461020d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b838110156101d25781810151838201526020016101ba565b50505050905090810190601f1680156101ff5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b50600160a060020a03821660009081526001602052604090205460ff90811690821681116102b457604051600160a060020a0384169067ffffffffffffffff670de0b6b3a764000060ff60026001878903010216021680156108fc02916000818181858888f19350505050158015610289573d6000803e3d6000fd5b50600160a060020a0383166000908152600160205260409020805460ff191660ff84161790556102d5565b600160a060020a0383166000908152600160205260409020805460ff191690555b505050565b600054600160a060020a031681565b60408051808201909152601181527f496e73756666696369656e742046756e640000000000000000000000000000006020820152671bc16d674ec8000034101561038f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382528381815181526020019150805190602001908083836000838110156101d25781810151838201526020016101ba565b5056fea165627a7a72305820dcdfb1bf37c0dddda1b2ec670fa6ef6c8913a8b4b6e815bf184697e5da7b0d1c0029` + +// DeployPuzzle deploys a new Ethereum contract, binding an instance of Puzzle to it. +func DeployPuzzle(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Puzzle, error) { + parsed, err := abi.JSON(strings.NewReader(PuzzleABI)) + if err != nil { + return common.Address{}, nil, nil, err + } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(PuzzleBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Puzzle{PuzzleCaller: PuzzleCaller{contract: contract}, PuzzleTransactor: PuzzleTransactor{contract: contract}, PuzzleFilterer: PuzzleFilterer{contract: contract}}, nil +} + +// Puzzle is an auto generated Go binding around an Ethereum contract. +type Puzzle struct { + PuzzleCaller // Read-only binding to the contract + PuzzleTransactor // Write-only binding to the contract + PuzzleFilterer // Log filterer for contract events +} + +// PuzzleCaller is an auto generated read-only Go binding around an Ethereum contract. +type PuzzleCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PuzzleTransactor is an auto generated write-only Go binding around an Ethereum contract. +type PuzzleTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PuzzleFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type PuzzleFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// PuzzleSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type PuzzleSession struct { + Contract *Puzzle // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PuzzleCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type PuzzleCallerSession struct { + Contract *PuzzleCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// PuzzleTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type PuzzleTransactorSession struct { + Contract *PuzzleTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// PuzzleRaw is an auto generated low-level Go binding around an Ethereum contract. +type PuzzleRaw struct { + Contract *Puzzle // Generic contract binding to access the raw methods on +} + +// PuzzleCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type PuzzleCallerRaw struct { + Contract *PuzzleCaller // Generic read-only contract binding to access the raw methods on +} + +// PuzzleTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type PuzzleTransactorRaw struct { + Contract *PuzzleTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewPuzzle creates a new instance of Puzzle, bound to a specific deployed contract. +func NewPuzzle(address common.Address, backend bind.ContractBackend) (*Puzzle, error) { + contract, err := bindPuzzle(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Puzzle{PuzzleCaller: PuzzleCaller{contract: contract}, PuzzleTransactor: PuzzleTransactor{contract: contract}, PuzzleFilterer: PuzzleFilterer{contract: contract}}, nil +} + +// NewPuzzleCaller creates a new read-only instance of Puzzle, bound to a specific deployed contract. +func NewPuzzleCaller(address common.Address, caller bind.ContractCaller) (*PuzzleCaller, error) { + contract, err := bindPuzzle(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &PuzzleCaller{contract: contract}, nil +} + +// NewPuzzleTransactor creates a new write-only instance of Puzzle, bound to a specific deployed contract. +func NewPuzzleTransactor(address common.Address, transactor bind.ContractTransactor) (*PuzzleTransactor, error) { + contract, err := bindPuzzle(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &PuzzleTransactor{contract: contract}, nil +} + +// NewPuzzleFilterer creates a new log filterer instance of Puzzle, bound to a specific deployed contract. +func NewPuzzleFilterer(address common.Address, filterer bind.ContractFilterer) (*PuzzleFilterer, error) { + contract, err := bindPuzzle(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &PuzzleFilterer{contract: contract}, nil +} + +// bindPuzzle binds a generic wrapper to an already deployed contract. +func bindPuzzle(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(PuzzleABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Puzzle *PuzzleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Puzzle.Contract.PuzzleCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Puzzle *PuzzleRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Puzzle.Contract.PuzzleTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Puzzle *PuzzleRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Puzzle.Contract.PuzzleTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Puzzle *PuzzleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + return _Puzzle.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Puzzle *PuzzleTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Puzzle.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Puzzle *PuzzleTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Puzzle.Contract.contract.Transact(opts, method, params...) +} + +// LevelMap is a free data retrieval call binding the contract method 0x11b88fef. +// +// Solidity: function level_map( address) constant returns(uint8) +func (_Puzzle *PuzzleCaller) LevelMap(opts *bind.CallOpts, arg0 common.Address) (uint8, error) { + var ( + ret0 = new(uint8) + ) + out := ret0 + err := _Puzzle.contract.Call(opts, out, "level_map", arg0) + return *ret0, err +} + +// LevelMap is a free data retrieval call binding the contract method 0x11b88fef. +// +// Solidity: function level_map( address) constant returns(uint8) +func (_Puzzle *PuzzleSession) LevelMap(arg0 common.Address) (uint8, error) { + return _Puzzle.Contract.LevelMap(&_Puzzle.CallOpts, arg0) +} + +// LevelMap is a free data retrieval call binding the contract method 0x11b88fef. +// +// Solidity: function level_map( address) constant returns(uint8) +func (_Puzzle *PuzzleCallerSession) LevelMap(arg0 common.Address) (uint8, error) { + return _Puzzle.Contract.LevelMap(&_Puzzle.CallOpts, arg0) +} + +// Manager is a free data retrieval call binding the contract method 0x481c6a75. +// +// Solidity: function manager() constant returns(address) +func (_Puzzle *PuzzleCaller) Manager(opts *bind.CallOpts) (common.Address, error) { + var ( + ret0 = new(common.Address) + ) + out := ret0 + err := _Puzzle.contract.Call(opts, out, "manager") + return *ret0, err +} + +// Manager is a free data retrieval call binding the contract method 0x481c6a75. +// +// Solidity: function manager() constant returns(address) +func (_Puzzle *PuzzleSession) Manager() (common.Address, error) { + return _Puzzle.Contract.Manager(&_Puzzle.CallOpts) +} + +// Manager is a free data retrieval call binding the contract method 0x481c6a75. +// +// Solidity: function manager() constant returns(address) +func (_Puzzle *PuzzleCallerSession) Manager() (common.Address, error) { + return _Puzzle.Contract.Manager(&_Puzzle.CallOpts) +} + +// Payout is a paid mutator transaction binding the contract method 0x158b4aa6. +// +// Solidity: function payout(player address, new_level uint8) returns() +func (_Puzzle *PuzzleTransactor) Payout(opts *bind.TransactOpts, player common.Address, new_level uint8) (*types.Transaction, error) { + return _Puzzle.contract.Transact(opts, "payout", player, new_level) +} + +// Payout is a paid mutator transaction binding the contract method 0x158b4aa6. +// +// Solidity: function payout(player address, new_level uint8) returns() +func (_Puzzle *PuzzleSession) Payout(player common.Address, new_level uint8) (*types.Transaction, error) { + return _Puzzle.Contract.Payout(&_Puzzle.TransactOpts, player, new_level) +} + +// Payout is a paid mutator transaction binding the contract method 0x158b4aa6. +// +// Solidity: function payout(player address, new_level uint8) returns() +func (_Puzzle *PuzzleTransactorSession) Payout(player common.Address, new_level uint8) (*types.Transaction, error) { + return _Puzzle.Contract.Payout(&_Puzzle.TransactOpts, player, new_level) +} + +// Play is a paid mutator transaction binding the contract method 0x93e84cd9. +// +// Solidity: function play() returns() +func (_Puzzle *PuzzleTransactor) Play(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Puzzle.contract.Transact(opts, "play") +} + +// Play is a paid mutator transaction binding the contract method 0x93e84cd9. +// +// Solidity: function play() returns() +func (_Puzzle *PuzzleSession) Play() (*types.Transaction, error) { + return _Puzzle.Contract.Play(&_Puzzle.TransactOpts) +} + +// Play is a paid mutator transaction binding the contract method 0x93e84cd9. +// +// Solidity: function play() returns() +func (_Puzzle *PuzzleTransactorSession) Play() (*types.Transaction, error) { + return _Puzzle.Contract.Play(&_Puzzle.TransactOpts) +} diff --git a/contracts/Puzzle.sol b/contracts/Puzzle.sol new file mode 100644 index 000000000..b3ce15d8a --- /dev/null +++ b/contracts/Puzzle.sol @@ -0,0 +1,37 @@ +pragma solidity >=0.4.22; +contract Puzzle { + string internal constant INSUFFICIENT_FUND_MESSAGE = "Insufficient Fund"; + string internal constant RESTRICTED_MESSAGE = "Unauthorized Access"; + address public manager; // The adress of the owner of this contract + mapping(address => uint8) public level_map; + + constructor() public payable { + manager = msg.sender; + } + + /** + * @dev The player enters into the current game session by + * paying at least 2 token. + */ + function play() public payable { + require(msg.value >= 2 ether, INSUFFICIENT_FUND_MESSAGE); + } + + /** + * @dev pay the player if they have crossed their last best level. + */ + function payout(address payable player, uint8 new_level) public payable restricted { + uint8 cur_level = level_map[player]; + if (new_level >= cur_level) { + player.transfer(2 * (new_level - cur_level + 1) * 1 ether); + level_map[player] = new_level; + } else { + delete level_map[player]; + } + } + + modifier restricted() { + require(msg.sender == manager, RESTRICTED_MESSAGE); + _; + } +} diff --git a/contracts/gen.sh b/contracts/gen.sh index cc7ed0d33..47f2b4add 100755 --- a/contracts/gen.sh +++ b/contracts/gen.sh @@ -1,3 +1,4 @@ -abigen -sol Lottery.sol -out Lottery.go --pkg contracts +# abigen -sol Lottery.sol -out Lottery.go --pkg contracts +abigen -sol Puzzle.sol -out Puzzle.go --pkg contracts # abigen -sol Faucet.sol -out Faucet.go --pkg contracts abigen -sol StakeLockContract.sol -out StakeLockContract.go --pkg contracts diff --git a/internal/utils/contract/constants.go b/internal/utils/contract/constants.go index 9f3b036af..7e30c7ad9 100644 --- a/internal/utils/contract/constants.go +++ b/internal/utils/contract/constants.go @@ -883,6 +883,22 @@ var DemoAccounts = [...]DeployAccount{ {Address: "0xd2Cb501B40D3a9a013A38267a4d2A4Cf6bD2CAa8", Private: "3c8642f7188e05acc4467d9e2aa7fd539e82aa90a5497257cf0ecbb98ed3b88f", Public: "0xd2Cb501B40D3a9a013A38267a4d2A4Cf6bD2CAa8"}, } +// PuzzleAccounts is the accounts used for lottery demo. +var PuzzleAccounts = [...]DeployAccount{ + {Address: "0xBE3958eD7A723Cb6fFF92b0B07682672B2AbD15A", Private: "0e9ee528c878a492ee6ae645ad31656239bb12536377aeff1e7aa521dd5848c9", Public: "0xBE3958eD7A723Cb6fFF92b0B07682672B2AbD15A"}, + {Address: "0x87A4c9209d7DD8aBDcE2285cB0FF4b2015A80f95", Private: "8d95c5b66b15a162c9bfcbfe0de041fe328e489f50af0fd9772a4251d090179c", Public: "0x87A4c9209d7DD8aBDcE2285cB0FF4b2015A80f95"}, + {Address: "0x92f65ed2bB5DaeA77c6Ca22BA38ca4B4c2443AbB", Private: "86ba356093c54f6557faa8aa28abd89fb843462226c6822ffcaf1bebfc572107", Public: "0x92f65ed2bB5DaeA77c6Ca22BA38ca4B4c2443AbB"}, + {Address: "0x352f4b24BCEDdA13B7263D347656f104Bc4Ac06F", Private: "9f4fb861c4b81285d5a0cd040ea937f7fcc33eba14cc0da550d201454d67f45e", Public: "0x352f4b24BCEDdA13B7263D347656f104Bc4Ac06F"}, + {Address: "0xfc7e61314D41511e783382bde2aF63a8Ba35942f", Private: "bc8a2cde3a38168d86c2e1f8d3246c4d13ff17446dc3b9954a6b5c158d1f587f", Public: "0xfc7e61314D41511e783382bde2aF63a8Ba35942f"}, + {Address: "0x1A5728b833E734a73e3dfB4dD72da35eB2fcd902", Private: "45172a91c871365754788ad8db59f7e24a848861c7cbfa69e4577885ddde06b5", Public: "0x1A5728b833E734a73e3dfB4dD72da35eB2fcd902"}, + {Address: "0xc4bac29A908FD312254bdf2Cc0Ad953eC3D3C9f8", Private: "8ef36cfe78ade686167d1b01674679e954f0f3fbdd5a5517229d8ce7e82c2a98", Public: "0xc4bac29A908FD312254bdf2Cc0Ad953eC3D3C9f8"}, + {Address: "0x450ee67054761078Dfa05C347A5dDbA0eEB40a1B", Private: "12614ab17446556bd355912b3d58f106776e6704c935c7d21e44a49dabc145d1", Public: "0x450ee67054761078Dfa05C347A5dDbA0eEB40a1B"}, + {Address: "0xA01d28C5B8555C999A4ccE0Ef1F766b74971469f", Private: "0501438b5597142e6b66fa5901d774d2c010e56457b4c2aefc9e0b3796dc6035", Public: "0xA01d28C5B8555C999A4ccE0Ef1F766b74971469f"}, + {Address: "0xa97a0bc3ab0615bd52f607Bc73f641225c753A1B", Private: "c97cd7c35a8f189fdcd05c0f3799343300272d64bb30914a839a97361e248c6b", Public: "0xa97a0bc3ab0615bd52f607Bc73f641225c753A1B"}, + {Address: "0x931F6E2f7D75b3A3674Db7f377270bC2D7E85a0b", Private: "b958996cef750ca565604e04c30034c8e77e06523751cac5cdc14ba22f01ca70", Public: "0x931F6E2f7D75b3A3674Db7f377270bC2D7E85a0b"}, + {Address: "0xB3AE88dF65D0871e9cF11798c1eC496C7A0109CC", Private: "c2cb484f137c71a5ccc45f9e46d579970667ee6d23833c03c1c2b48900807b0c", Public: "0xB3AE88dF65D0871e9cF11798c1eC496C7A0109CC"}, +} + // NewNodeAccounts is the accounts used for new node to stake and join the network. var NewNodeAccounts = [...]DeployAccount{ {Address: "0xa2F8b3bf3ea7b46576F3a7be105d7F74186df42d", Private: "8ca12c7fa1f54406b518cc3ca78bad9c3fb7206fad3a258d6583e0a31a52e2ce", Public: "0xa2F8b3bf3ea7b46576F3a7be105d7F74186df42d"}, diff --git a/node/contract.go b/node/contract.go index 6295ee363..beba54919 100644 --- a/node/contract.go +++ b/node/contract.go @@ -32,6 +32,7 @@ const ( scFaucet builtInSC = iota scStaking scLottery + scPuzzle ) // AddStakingContractToPendingTransactions adds the deposit smart contract the genesis block. @@ -223,6 +224,10 @@ func (node *Node) AddContractKeyAndAddress(t builtInSC) { // lottery lotteryPriKey, _ := crypto.HexToECDSA(contract_constants.DemoAccounts[0].Private) node.DemoContractAddress = crypto.CreateAddress(crypto.PubkeyToAddress(lotteryPriKey.PublicKey), uint64(0)) + case scPuzzle: + // puzzle + puzzlePriKey, _ := crypto.HexToECDSA(contract_constants.PuzzleAccounts[0].Private) + node.PuzzleContractAddress = crypto.CreateAddress(crypto.PubkeyToAddress(puzzlePriKey.PublicKey), uint64(0)) default: utils.GetLogInstance().Error("AddContractKeyAndAddress", "unknown SC", t) } diff --git a/node/demo_contract.go b/node/demo_contract.go index 7e6028321..3e7b4542b 100644 --- a/node/demo_contract.go +++ b/node/demo_contract.go @@ -1,6 +1,5 @@ package node -// CreateTransactionForEnterMethod creates transaction to call enter method of lottery contract. import ( "fmt" "math" @@ -23,6 +22,7 @@ const ( Enter = "enter" PickWinner = "pickWinner" GetPlayers = "getPlayers" + PuzzleFund = 100000000 ) // AddLotteryContract adds the demo lottery contract the genesis block. diff --git a/node/node_error.go b/node/errors.go similarity index 53% rename from node/node_error.go rename to node/errors.go index 096152559..18c262e71 100644 --- a/node/node_error.go +++ b/node/errors.go @@ -5,4 +5,6 @@ import "errors" var ( // ErrLotteryAppFailed is the error when a transaction failed to process lottery app. ErrLotteryAppFailed = errors.New("Failed to process lottery app transaction") + // ErrPuzzleInsufficientFund is the error when a user does not have sufficient fund to enter. + ErrPuzzleInsufficientFund = errors.New("You do not have sufficient fund to play") ) diff --git a/node/node.go b/node/node.go index a4fb3c89f..e400f4a2d 100644 --- a/node/node.go +++ b/node/node.go @@ -139,6 +139,10 @@ type Node struct { DemoContractAddress common.Address LotteryManagerPrivateKey *ecdsa.PrivateKey + // Puzzle account. + PuzzleContractAddress common.Address + PuzzleManagerPrivateKey *ecdsa.PrivateKey + //Node Account AccountKey *ecdsa.PrivateKey @@ -282,9 +286,11 @@ func New(host p2p.Host, consensusObj *consensus.Consensus, db ethdb.Database, is // TODO(minhdoan): Think of a better approach to deploy smart contract. // This is temporary for demo purpose. node.AddLotteryContract() + node.AddPuzzleContract() } else { node.AddContractKeyAndAddress(scStaking) node.AddContractKeyAndAddress(scLottery) + node.AddContractKeyAndAddress(scPuzzle) } } } diff --git a/node/puzzle_contract.go b/node/puzzle_contract.go new file mode 100644 index 000000000..839019ae1 --- /dev/null +++ b/node/puzzle_contract.go @@ -0,0 +1,146 @@ +package node + +import ( + "fmt" + "math/big" + "os" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/harmony-one/harmony/contracts" + "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/internal/utils" + contract_constants "github.com/harmony-one/harmony/internal/utils/contract" +) + +// Constants for puzzle. +const ( + Play = "play" + Payout = "payout" +) + +// OneEther represents one ether. +var OneEther = big.NewInt(params.Ether) + +// AddPuzzleContract adds the demo puzzle contract the genesis block. +func (node *Node) AddPuzzleContract() { + // Add a puzzle demo contract. + priKey, err := crypto.HexToECDSA(contract_constants.PuzzleAccounts[0].Private) + if err != nil { + utils.GetLogInstance().Error("Error when creating private key for puzzle demo contract") + // Exit here to recognize the coding working. + // Basically we will remove this logic when launching so it's fine for now. + os.Exit(1) + } + + dataEnc := common.FromHex(contracts.PuzzleBin) + // Unsigned transaction to avoid the case of transaction address. + + contractFunds := big.NewInt(PuzzleFund) + contractFunds = contractFunds.Mul(contractFunds, big.NewInt(params.Ether)) + demoContract, _ := types.SignTx( + types.NewContractCreation(uint64(0), node.Consensus.ShardID, contractFunds, params.TxGasContractCreation*10, nil, dataEnc), + types.HomesteadSigner{}, + priKey) + node.PuzzleContractAddress = crypto.CreateAddress(crypto.PubkeyToAddress(priKey.PublicKey), uint64(0)) + node.PuzzleManagerPrivateKey = priKey + node.addPendingTransactions(types.Transactions{demoContract}) +} + +// CreateTransactionForPlayMethod generates transaction for enter method and add it into pending tx list. +func (node *Node) CreateTransactionForPlayMethod(priKey string) error { + var err error + toAddress := node.PuzzleContractAddress + + abi, err := abi.JSON(strings.NewReader(contracts.PuzzleABI)) + if err != nil { + utils.GetLogInstance().Error("puzzle-play: Failed to generate staking contract's ABI", "error", err) + return err + } + bytesData, err := abi.Pack(Play) + if err != nil { + utils.GetLogInstance().Error("puzzle-play: Failed to generate ABI function bytes data", "error", err) + return err + } + + key, err := crypto.HexToECDSA(priKey) + address := crypto.PubkeyToAddress(key.PublicKey) + balance, err := node.GetBalanceOfAddress(address) + if err != nil { + utils.GetLogInstance().Error("puzzle-play: can not get address", "error", err) + return err + } else if balance.Cmp(OneEther) == -1 { + utils.GetLogInstance().Error("puzzle-play: insufficient fund", "error", err) + return ErrPuzzleInsufficientFund + } + nonce := node.GetNonceOfAddress(address) + tx := types.NewTransaction( + nonce, + toAddress, + 0, + OneEther, + params.TxGas*10, + nil, + bytesData, + ) + + if err != nil { + utils.GetLogInstance().Error("puzzle-play: Failed to get private key", "error", err) + return err + } + if signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, key); err == nil { + node.addPendingTransactions(types.Transactions{signedTx}) + return nil + } + utils.GetLogInstance().Error("puzzle-play: Unable to call enter method", "error", err) + return err +} + +// CreateTransactionForPayoutMethod generates transaction for payout method and add it into pending tx list. +func (node *Node) CreateTransactionForPayoutMethod(address string, newLevel int) error { + var err error + toAddress := node.PuzzleContractAddress + + abi, err := abi.JSON(strings.NewReader(contracts.PuzzleABI)) + if err != nil { + utils.GetLogInstance().Error("Failed to generate staking contract's ABI", "error", err) + return err + } + // add params for address payable player, uint8 new_level + // TODO(minh, rj) + bytesData, err := abi.Pack(Payout) + if err != nil { + utils.GetLogInstance().Error("Failed to generate ABI function bytes data", "error", err) + return err + } + + key := node.PuzzleManagerPrivateKey + if key == nil { + return fmt.Errorf("PuzzleManagerPrivateKey is nil") + } + nonce := node.GetNonceOfAddress(crypto.PubkeyToAddress(key.PublicKey)) + Amount := big.NewInt(0) + tx := types.NewTransaction( + nonce, + toAddress, + 0, + Amount, + params.TxGas*10000, + nil, + bytesData, + ) + + if err != nil { + utils.GetLogInstance().Error("Failed to get private key", "error", err) + return err + } + if signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, key); err == nil { + node.addPendingTransactions(types.Transactions{signedTx}) + return nil + } + utils.GetLogInstance().Error("Unable to call enter method", "error", err) + return err +} diff --git a/node/service_setup.go b/node/service_setup.go index 304ea51e6..ee302fa12 100644 --- a/node/service_setup.go +++ b/node/service_setup.go @@ -64,7 +64,9 @@ func (node *Node) setupForBeaconLeader() { // Register client new support service. // TODO(minhdoan): Also consider provide clientsupport/restclientsupport for other shards in the future. node.serviceManager.RegisterService(service.RestClientSupport, restclientsupport.New( - node.CreateTransactionForEnterMethod, node.GetResult, node.CreateTransactionForPickWinner, node.CallFaucetContract, node.GetBalanceOfAddress)) + node.CreateTransactionForEnterMethod, node.GetResult, + node.CreateTransactionForPickWinner, node.CallFaucetContract, node.GetBalanceOfAddress, + node.CreateTransactionForPlayMethod, node.CreateTransactionForPayoutMethod)) // Register randomness service node.serviceManager.RegisterService(service.Randomness, randomness.New(node.DRand)) // Register explorer service.