package node import ( "crypto/ecdsa" "math/big" "strings" "sync/atomic" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/common/denominations" "github.com/harmony-one/harmony/contracts" "github.com/harmony-one/harmony/core/types" common2 "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/utils" ) // Constants related to smart contract. const ( FaucetContractFund = 80000000 ) // BuiltInSC is the type of built-in smart contract in blockchain type builtInSC uint // List of smart contract type built-in const ( scFaucet builtInSC = iota ) // GetNonceOfAddress returns nonce of an address. func (node *Node) GetNonceOfAddress(address common.Address) uint64 { state, err := node.Blockchain().State() if err != nil { utils.Logger().Error().Err(err).Msg("Failed to get chain state") return 0 } return state.GetNonce(address) } // GetBalanceOfAddress returns balance of an address. func (node *Node) GetBalanceOfAddress(address common.Address) (*big.Int, error) { state, err := node.Blockchain().State() if err != nil { utils.Logger().Error().Err(err).Msg("Failed to get chain state") return nil, err } balance := big.NewInt(0) balance.SetBytes(state.GetBalance(address).Bytes()) return balance, nil } // AddFaucetContractToPendingTransactions adds the faucet contract the genesis block. func (node *Node) AddFaucetContractToPendingTransactions() { // Add a contract deployment transactionv priKey := node.ContractDeployerKey dataEnc := common.FromHex(contracts.FaucetBin) // Unsigned transaction to avoid the case of transaction address. contractFunds := big.NewInt(FaucetContractFund) contractFunds = contractFunds.Mul(contractFunds, big.NewInt(denominations.One)) mycontracttx, _ := types.SignTx( types.NewContractCreation(uint64(0), node.Consensus.ShardID, contractFunds, params.TxGasContractCreation*10, nil, dataEnc), types.HomesteadSigner{}, priKey) node.ContractAddresses = append(node.ContractAddresses, crypto.CreateAddress(crypto.PubkeyToAddress(priKey.PublicKey), uint64(0))) node.addPendingTransactions(types.Transactions{mycontracttx}) } // CallFaucetContract invokes the faucet contract to give the walletAddress initial money func (node *Node) CallFaucetContract(address common.Address) common.Hash { if node.NodeConfig.GetNetworkType() == nodeconfig.Mainnet { return common.Hash{} } // Temporary code to workaround explorer issue for searching new addresses (https://github.com/harmony-one/harmony/issues/503) nonce := atomic.AddUint64(&node.ContractDeployerCurrentNonce, 1) tx, _ := types.SignTx(types.NewTransaction(nonce-1, address, node.Consensus.ShardID, big.NewInt(0), params.TxGasContractCreation*10, nil, nil), types.HomesteadSigner{}, node.ContractDeployerKey) utils.Logger().Info().Str("Address", common2.MustAddressToBech32(address)).Msg("Sending placeholder token to ") node.addPendingTransactions(types.Transactions{tx}) // END Temporary code nonce = atomic.AddUint64(&node.ContractDeployerCurrentNonce, 1) return node.callGetFreeTokenWithNonce(address, nonce-1) } func (node *Node) callGetFreeToken(address common.Address) common.Hash { nonce := atomic.AddUint64(&node.ContractDeployerCurrentNonce, 1) return node.callGetFreeTokenWithNonce(address, nonce-1) } func (node *Node) callGetFreeTokenWithNonce(address common.Address, nonce uint64) common.Hash { abi, err := abi.JSON(strings.NewReader(contracts.FaucetABI)) if err != nil { utils.Logger().Error().Err(err).Msg("Failed to generate faucet contract's ABI") return common.Hash{} } bytesData, err := abi.Pack("request", address) if err != nil { utils.Logger().Error().Err(err).Msg("Failed to generate ABI function bytes data") return common.Hash{} } if len(node.ContractAddresses) == 0 { utils.Logger().Error().Err(err).Msg("Failed to find the contract address") return common.Hash{} } tx, _ := types.SignTx(types.NewTransaction(nonce, node.ContractAddresses[0], node.Consensus.ShardID, big.NewInt(0), params.TxGasContractCreation*10, nil, bytesData), types.HomesteadSigner{}, node.ContractDeployerKey) utils.Logger().Info().Str("Address", common2.MustAddressToBech32(address)).Msg("Sending Free Token to ") node.addPendingTransactions(types.Transactions{tx}) return tx.Hash() } // AddContractKeyAndAddress is used to add smart contract related information when node restart and resume with previous state // It supports three kinds of on-chain smart contracts for now. func (node *Node) AddContractKeyAndAddress(t builtInSC) { switch t { case scFaucet: // faucet contract contractDeployerKey, _ := ecdsa.GenerateKey(crypto.S256(), strings.NewReader("Test contract key string stream that is fixed so that generated test key are deterministic every time")) node.ContractDeployerKey = contractDeployerKey node.ContractAddresses = append(node.ContractAddresses, crypto.CreateAddress(crypto.PubkeyToAddress(contractDeployerKey.PublicKey), uint64(0))) default: utils.Logger().Error().Interface("unknown SC", t).Msg("AddContractKeyAndAddress") } }