The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
woop/node/contract.go

233 lines
9.2 KiB

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/common/math"
"github.com/ethereum/go-ethereum/crypto"
"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/contracts/structs"
"github.com/harmony-one/harmony/core/types"
common2 "github.com/harmony-one/harmony/internal/common"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/genesis"
"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
scStaking
)
// AddStakingContractToPendingTransactions adds the deposit smart contract the genesis block.
func (node *Node) AddStakingContractToPendingTransactions() {
// Add a contract deployment transaction
//Generate contract key and associate funds with the smart contract
priKey := genesis.GenesisBeaconAccountPriKey
contractAddress := crypto.PubkeyToAddress(priKey.PublicKey)
//Initially the smart contract should have minimal funds.
contractFunds := big.NewInt(0)
contractFunds = contractFunds.Mul(contractFunds, big.NewInt(denominations.One))
dataEnc := common.FromHex(contracts.StakeLockContractBin)
// Unsigned transaction to avoid the case of transaction address.
mycontracttx, _ := types.SignTx(types.NewContractCreation(uint64(0), node.Consensus.ShardID, contractFunds, params.TxGasContractCreation*100, nil, dataEnc), types.HomesteadSigner{}, priKey)
//node.StakingContractAddress = crypto.CreateAddress(contractAddress, uint64(0))
node.StakingContractAddress = node.generateDeployedStakingContractAddress(contractAddress)
node.addPendingTransactions(types.Transactions{mycontracttx})
}
// In order to get the deployed contract address of a contract, we need to find the nonce of the address that created it.
// (Refer: https://solidity.readthedocs.io/en/v0.5.3/introduction-to-smart-contracts.html#index-8)
// Then we can (re)create the deployed address. Trivially, this is 0 for us.
// The deployed contract address can also be obtained via the receipt of the contract creating transaction.
func (node *Node) generateDeployedStakingContractAddress(contractAddress common.Address) common.Address {
//Correct Way 1:
//node.SendTx(mycontracttx)
//receipts := node.worker.GetCurrentReceipts()
//deployedcontractaddress = receipts[len(receipts)-1].ContractAddress //get the address from the receipt
//Correct Way 2:
//nonce := GetNonce(contractAddress)
//deployedAddress := crypto.CreateAddress(contractAddress, uint64(nonce))
nonce := 0
return crypto.CreateAddress(contractAddress, uint64(nonce))
}
// QueryStakeInfo queries the stake info from the stake contract.
func (node *Node) QueryStakeInfo() *structs.StakeInfoReturnValue {
abi, err := abi.JSON(strings.NewReader(contracts.StakeLockContractABI))
if err != nil {
utils.Logger().Error().Err(err).Msg("Failed to generate staking contract's ABI")
return nil
}
bytesData, err := abi.Pack("listLockedAddresses")
if err != nil {
utils.Logger().Error().Err(err).Msg("Failed to generate ABI function bytes data")
return nil
}
priKey := genesis.GenesisBeaconAccountPriKey
deployerAddress := crypto.PubkeyToAddress(priKey.PublicKey)
state, err := node.Blockchain().State()
if err != nil {
utils.Logger().Error().Err(err).Msg("Failed to get blockchain state")
return nil
}
stakingContractAddress := crypto.CreateAddress(deployerAddress, uint64(0))
tx := types.NewTransaction(
state.GetNonce(deployerAddress),
stakingContractAddress,
node.NodeConfig.ShardID,
nil,
math.MaxUint64,
nil,
bytesData,
)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, priKey)
if err != nil {
utils.Logger().Error().Err(err).Msg("Failed to sign contract call tx")
return nil
}
output, err := node.ContractCaller.CallContract(signedTx)
if err != nil {
utils.Logger().Error().Err(err).Msg("Failed to call staking contract")
return nil
}
ret := &structs.StakeInfoReturnValue{}
err = abi.Unpack(ret, "listLockedAddresses", output)
if err != nil {
utils.Logger().Error().Err(err).Msg("Failed to unpack stake info")
return nil
}
return ret
}
func (node *Node) getDeployedStakingContract() common.Address {
if node.NodeConfig.GetNetworkType() == nodeconfig.Mainnet {
return common.Address{}
}
return node.StakingContractAddress
}
// 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
}
return state.GetBalance(address), 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)))
case scStaking:
// staking contract
node.CurrentStakes = make(map[common.Address]*structs.StakeInfo)
stakingPrivKey := genesis.GenesisBeaconAccountPriKey
node.StakingContractAddress = crypto.CreateAddress(crypto.PubkeyToAddress(stakingPrivKey.PublicKey), uint64(0))
default:
utils.Logger().Error().Interface("unknown SC", t).Msg("AddContractKeyAndAddress")
}
}