github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/contract_comm/random/random.go (about) 1 package random 2 3 import ( 4 "strings" 5 6 "github.com/ethereum/go-ethereum/accounts/abi" 7 "github.com/ethereum/go-ethereum/common" 8 "github.com/ethereum/go-ethereum/common/hexutil" 9 "github.com/ethereum/go-ethereum/consensus" 10 "github.com/ethereum/go-ethereum/contract_comm" 11 "github.com/ethereum/go-ethereum/contract_comm/errors" 12 "github.com/ethereum/go-ethereum/core/types" 13 "github.com/ethereum/go-ethereum/core/vm" 14 "github.com/ethereum/go-ethereum/crypto" 15 "github.com/ethereum/go-ethereum/ethdb" 16 "github.com/ethereum/go-ethereum/log" 17 "github.com/ethereum/go-ethereum/params" 18 ) 19 20 const ( 21 // This is taken from celo-monorepo/packages/protocol/build/<env>/contracts/Random.json 22 revealAndCommitABI = `[ 23 { 24 "constant": false, 25 "inputs": [ 26 { 27 "name": "randomness", 28 "type": "bytes32" 29 }, 30 { 31 "name": "newCommitment", 32 "type": "bytes32" 33 }, 34 { 35 "name": "proposer", 36 "type": "address" 37 } 38 ], 39 "name": "revealAndCommit", 40 "outputs": [], 41 "payable": false, 42 "stateMutability": "nonpayable", 43 "type": "function" 44 } 45 ]` 46 commitmentsAbi = `[ 47 { 48 "constant": true, 49 "inputs": [ 50 { 51 "name": "", 52 "type": "address" 53 } 54 ], 55 "name": "commitments", 56 "outputs": [ 57 { 58 "name": "", 59 "type": "bytes32" 60 } 61 ], 62 "payable": false, 63 "stateMutability": "view", 64 "type": "function" 65 } 66 ]` 67 computeCommitmentAbi = `[ 68 { 69 "constant": true, 70 "inputs": [ 71 { 72 "name": "randomness", 73 "type": "bytes32" 74 } 75 ], 76 "name": "computeCommitment", 77 "outputs": [ 78 { 79 "name": "", 80 "type": "bytes32" 81 } 82 ], 83 "payable": false, 84 "stateMutability": "view", 85 "type": "function" 86 } 87 ]` 88 randomAbi = `[ 89 { 90 "constant": true, 91 "inputs": [], 92 "name": "random", 93 "outputs": [ 94 { 95 "name": "", 96 "type": "bytes32" 97 } 98 ], 99 "payable": false, 100 "stateMutability": "view", 101 "type": "function" 102 } 103 ]` 104 ) 105 106 var ( 107 revealAndCommitFuncABI, _ = abi.JSON(strings.NewReader(revealAndCommitABI)) 108 commitmentsFuncABI, _ = abi.JSON(strings.NewReader(commitmentsAbi)) 109 computeCommitmentFuncABI, _ = abi.JSON(strings.NewReader(computeCommitmentAbi)) 110 randomFuncABI, _ = abi.JSON(strings.NewReader(randomAbi)) 111 zeroValue = common.Big0 112 dbRandomnessPrefix = []byte("db-randomness-prefix") 113 ) 114 115 func commitmentDbLocation(commitment common.Hash) []byte { 116 return append(dbRandomnessPrefix, commitment.Bytes()...) 117 } 118 119 func address() *common.Address { 120 randomAddress, err := contract_comm.GetRegisteredAddress(params.RandomRegistryId, nil, nil) 121 if err == errors.ErrSmartContractNotDeployed || err == errors.ErrRegistryContractNotDeployed { 122 log.Debug("Registry address lookup failed", "err", err, "contract", hexutil.Encode(params.RandomRegistryId[:])) 123 } else if err != nil { 124 log.Error(err.Error()) 125 } 126 return randomAddress 127 } 128 129 func IsRunning() bool { 130 randomAddress := address() 131 return randomAddress != nil && *randomAddress != common.ZeroAddress 132 } 133 134 // GetLastRandomness returns up the last randomness we committed to by first 135 // looking up our last commitment in the smart contract, and then finding the 136 // corresponding preimage in a (commitment => randomness) mapping we keep in the 137 // database. 138 func GetLastRandomness(coinbase common.Address, db *ethdb.Database, header *types.Header, state vm.StateDB, chain consensus.ChainReader, seed []byte) (common.Hash, error) { 139 lastCommitment := common.Hash{} 140 _, err := contract_comm.MakeStaticCall(params.RandomRegistryId, commitmentsFuncABI, "commitments", []interface{}{coinbase}, &lastCommitment, params.MaxGasForCommitments, header, state) 141 if err != nil { 142 log.Error("Failed to get last commitment", "err", err) 143 return lastCommitment, err 144 } 145 146 if (lastCommitment == common.Hash{}) { 147 log.Debug("Unable to find last randomness commitment in smart contract") 148 return common.Hash{}, nil 149 } 150 151 parentBlockHashBytes, err := (*db).Get(commitmentDbLocation(lastCommitment)) 152 if err != nil { 153 log.Error("Failed to get last block proposed from database", "commitment", lastCommitment.Hex(), "err", err) 154 parentBlockHash := header.ParentHash 155 for { 156 blockHeader := chain.GetHeaderByHash(parentBlockHash) 157 parentBlockHash = blockHeader.ParentHash 158 if blockHeader.Coinbase == coinbase { 159 break 160 } 161 } 162 parentBlockHashBytes = parentBlockHash.Bytes() 163 } 164 return crypto.Keccak256Hash(append(seed, parentBlockHashBytes...)), nil 165 } 166 167 // GenerateNewRandomnessAndCommitment generates a new random number and a corresponding commitment. 168 // The random number is stored in the database, keyed by the corresponding commitment. 169 func GenerateNewRandomnessAndCommitment(header *types.Header, state vm.StateDB, db *ethdb.Database, seed []byte) (common.Hash, error) { 170 commitment := common.Hash{} 171 randomness := crypto.Keccak256Hash(append(seed, header.ParentHash.Bytes()...)) 172 // TODO(asa): Make an issue to not have to do this via StaticCall 173 _, err := contract_comm.MakeStaticCall(params.RandomRegistryId, computeCommitmentFuncABI, "computeCommitment", []interface{}{randomness}, &commitment, params.MaxGasForComputeCommitment, header, state) 174 err = (*db).Put(commitmentDbLocation(commitment), header.ParentHash.Bytes()) 175 if err != nil { 176 log.Error("Failed to save last block parentHash to the database", "err", err) 177 } 178 return commitment, err 179 } 180 181 // RevealAndCommit performs an internal call to the EVM that reveals a 182 // proposer's previously committed to randomness, and commits new randomness for 183 // a future block. 184 func RevealAndCommit(randomness, newCommitment common.Hash, proposer common.Address, header *types.Header, state vm.StateDB) error { 185 args := []interface{}{randomness, newCommitment, proposer} 186 log.Trace("Revealing and committing randomness", "randomness", randomness.Hex(), "commitment", newCommitment.Hex()) 187 _, err := contract_comm.MakeCall(params.RandomRegistryId, revealAndCommitFuncABI, "revealAndCommit", args, nil, params.MaxGasForRevealAndCommit, zeroValue, header, state, true) 188 return err 189 } 190 191 // Random performs an internal call to the EVM to retrieve the current randomness from the official Random contract. 192 func Random(header *types.Header, state vm.StateDB) (common.Hash, error) { 193 randomness := common.Hash{} 194 _, err := contract_comm.MakeStaticCall(params.RandomRegistryId, randomFuncABI, "random", []interface{}{}, &randomness, params.MaxGasForComputeCommitment, header, state) 195 return randomness, err 196 }