prefetch.go 9.11 KB
Newer Older
1
//go:build !mips
George Hotz's avatar
George Hotz committed
2
// +build !mips
3

George Hotz's avatar
George Hotz committed
4 5 6
package oracle

import (
7
	"bytes"
George Hotz's avatar
George Hotz committed
8
	"encoding/hex"
9
	"encoding/json"
George Hotz's avatar
George Hotz committed
10
	"fmt"
11
	"io"
12
	"io/ioutil"
George Hotz's avatar
George Hotz committed
13
	"log"
George Hotz's avatar
George Hotz committed
14
	"math/big"
15
	"net/http"
16
	"os"
George Hotz's avatar
George Hotz committed
17 18

	"github.com/ethereum/go-ethereum/common"
19
	"github.com/ethereum/go-ethereum/common/hexutil"
20
	"github.com/ethereum/go-ethereum/core/types"
George Hotz's avatar
George Hotz committed
21
	"github.com/ethereum/go-ethereum/crypto"
22
	"github.com/ethereum/go-ethereum/rlp"
George Hotz's avatar
George Hotz committed
23 24
)

25
type jsonreq struct {
George Hotz's avatar
George Hotz committed
26 27 28 29
	Jsonrpc string        `json:"jsonrpc"`
	Method  string        `json:"method"`
	Params  []interface{} `json:"params"`
	Id      uint64        `json:"id"`
30 31 32 33 34 35 36 37
}

type jsonresp struct {
	Jsonrpc string        `json:"jsonrpc"`
	Id      uint64        `json:"id"`
	Result  AccountResult `json:"result"`
}

George Hotz's avatar
George Hotz committed
38 39 40 41 42 43
type jsonresps struct {
	Jsonrpc string `json:"jsonrpc"`
	Id      uint64 `json:"id"`
	Result  string `json:"result"`
}

George Hotz's avatar
George Hotz committed
44 45 46 47 48 49
type jsonrespi struct {
	Jsonrpc string         `json:"jsonrpc"`
	Id      uint64         `json:"id"`
	Result  hexutil.Uint64 `json:"result"`
}

50 51 52 53 54 55
type jsonrespt struct {
	Jsonrpc string `json:"jsonrpc"`
	Id      uint64 `json:"id"`
	Result  Header `json:"result"`
}

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
// Result structs for GetProof
type AccountResult struct {
	Address      common.Address  `json:"address"`
	AccountProof []string        `json:"accountProof"`
	Balance      *hexutil.Big    `json:"balance"`
	CodeHash     common.Hash     `json:"codeHash"`
	Nonce        hexutil.Uint64  `json:"nonce"`
	StorageHash  common.Hash     `json:"storageHash"`
	StorageProof []StorageResult `json:"storageProof"`
}

type StorageResult struct {
	Key   string       `json:"key"`
	Value *hexutil.Big `json:"value"`
	Proof []string     `json:"proof"`
}

// Account is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
type Account struct {
	Nonce    uint64
	Balance  *big.Int
	Root     common.Hash // merkle root of the storage trie
	CodeHash []byte
}

82 83 84
//var nodeUrl = "http://192.168.1.213:8545"
var nodeUrl = "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"

George Hotz's avatar
George Hotz committed
85
func toFilename(key string) string {
86
	return fmt.Sprintf("/tmp/eth/json_%s", key)
George Hotz's avatar
George Hotz committed
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
}

func cacheRead(key string) []byte {
	dat, err := ioutil.ReadFile(toFilename(key))
	if err == nil {
		return dat
	}
	panic("cache missing")
}

func cacheExists(key string) bool {
	_, err := os.Stat(toFilename(key))
	return err == nil
}

func cacheWrite(key string, value []byte) {
George Hotz's avatar
George Hotz committed
103
	ioutil.WriteFile(toFilename(key), value, 0644)
George Hotz's avatar
George Hotz committed
104 105
}

106 107 108 109 110 111 112 113 114 115 116 117
func getAPI(jsonData []byte) io.Reader {
	key := hexutil.Encode(crypto.Keccak256(jsonData))
	if cacheExists(key) {
		return bytes.NewReader(cacheRead(key))
	}
	resp, _ := http.Post(nodeUrl, "application/json", bytes.NewBuffer(jsonData))
	defer resp.Body.Close()
	ret, _ := ioutil.ReadAll(resp.Body)
	cacheWrite(key, ret)
	return bytes.NewReader(ret)
}

118 119 120 121 122 123
var unhashMap = make(map[common.Hash]common.Address)

func unhash(addrHash common.Hash) common.Address {
	return unhashMap[addrHash]
}

George Hotz's avatar
George Hotz committed
124
var cached = make(map[string]bool)
125

126
func PrefetchStorage(blockNumber *big.Int, addr common.Address, skey common.Hash, postProcess func(map[common.Hash][]byte)) {
127
	key := fmt.Sprintf("proof_%d_%s_%s", blockNumber, addr, skey)
128 129 130
	if cached[key] {
		return
	}
131
	cached[key] = true
132

133
	ap := getProofAccount(blockNumber, addr, skey, true)
134
	//fmt.Println("PrefetchStorage", blockNumber, addr, skey, len(ap))
135
	newPreimages := make(map[common.Hash][]byte)
136 137 138 139
	for _, s := range ap {
		ret, _ := hex.DecodeString(s[2:])
		hash := crypto.Keccak256Hash(ret)
		//fmt.Println("   ", i, hash)
140 141 142 143 144 145 146 147 148
		newPreimages[hash] = ret
	}

	if postProcess != nil {
		postProcess(newPreimages)
	}

	for hash, val := range newPreimages {
		preimages[hash] = val
149 150 151
	}
}

152
func PrefetchAccount(blockNumber *big.Int, addr common.Address, postProcess func(map[common.Hash][]byte)) {
153
	key := fmt.Sprintf("proof_%d_%s", blockNumber, addr)
George Hotz's avatar
George Hotz committed
154 155 156 157 158
	if cached[key] {
		return
	}
	cached[key] = true

159
	ap := getProofAccount(blockNumber, addr, common.Hash{}, false)
160
	newPreimages := make(map[common.Hash][]byte)
161 162 163
	for _, s := range ap {
		ret, _ := hex.DecodeString(s[2:])
		hash := crypto.Keccak256Hash(ret)
164 165 166 167 168 169 170 171 172
		newPreimages[hash] = ret
	}

	if postProcess != nil {
		postProcess(newPreimages)
	}

	for hash, val := range newPreimages {
		preimages[hash] = val
173 174 175
	}
}

176
func PrefetchCode(blockNumber *big.Int, addrHash common.Hash) {
177 178 179 180 181 182
	key := fmt.Sprintf("code_%d_%s", blockNumber, addrHash)
	if cached[key] {
		return
	}
	cached[key] = true
	ret := getProvedCodeBytes(blockNumber, addrHash)
183 184 185 186
	hash := crypto.Keccak256Hash(ret)
	preimages[hash] = ret
}

George Hotz's avatar
George Hotz committed
187
var inputs [8]common.Hash
188 189

func Input(index int) common.Hash {
George Hotz's avatar
George Hotz committed
190 191 192
	if index < 0 || index > 5 {
		panic("bad input index")
	}
193 194 195
	return inputs[index]
}

George Hotz's avatar
George Hotz committed
196
func Output(output common.Hash, receipts common.Hash) {
197 198 199 200
	if receipts != inputs[7] {
		fmt.Println("WARNING, receipts don't match", receipts, "!=", inputs[7])
	}
	if output == inputs[6] {
201 202
		fmt.Println("good transition")
	} else {
203
		fmt.Println(output, "!=", inputs[6])
204 205 206 207
		panic("BAD transition :((")
	}
}

George Hotz's avatar
George Hotz committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func prefetchUncles(blockHash common.Hash, uncleHash common.Hash, hasher types.TrieHasher) {
	jr := jsonrespi{}
	{
		r := jsonreq{Jsonrpc: "2.0", Method: "eth_getUncleCountByBlockHash", Id: 1}
		r.Params = make([]interface{}, 1)
		r.Params[0] = blockHash.Hex()
		jsonData, _ := json.Marshal(r)
		check(json.NewDecoder(getAPI(jsonData)).Decode(&jr))
	}

	var uncles []*types.Header
	for u := 0; u < int(jr.Result); u++ {
		jr2 := jsonrespt{}
		{
			r := jsonreq{Jsonrpc: "2.0", Method: "eth_getUncleByBlockHashAndIndex", Id: 1}
			r.Params = make([]interface{}, 2)
			r.Params[0] = blockHash.Hex()
			r.Params[1] = fmt.Sprintf("0x%x", u)
			jsonData, _ := json.Marshal(r)

			/*a, _ := ioutil.ReadAll(getAPI(jsonData))
			fmt.Println(string(a))*/

			check(json.NewDecoder(getAPI(jsonData)).Decode(&jr2))
		}
		uncleHeader := jr2.Result.ToHeader()
		uncles = append(uncles, &uncleHeader)
		//fmt.Println(uncleHeader)
		//fmt.Println(jr2.Result)
	}

	unclesRlp, _ := rlp.EncodeToBytes(uncles)
	hash := crypto.Keccak256Hash(unclesRlp)

	if hash != uncleHash {
		panic("wrong uncle hash")
	}

	preimages[hash] = unclesRlp
}

255 256 257 258 259 260 261 262 263 264 265
func PrefetchBlock(blockNumber *big.Int, startBlock bool, hasher types.TrieHasher) {
	r := jsonreq{Jsonrpc: "2.0", Method: "eth_getBlockByNumber", Id: 1}
	r.Params = make([]interface{}, 2)
	r.Params[0] = fmt.Sprintf("0x%x", blockNumber.Int64())
	r.Params[1] = true
	jsonData, _ := json.Marshal(r)

	/*dat, _ := ioutil.ReadAll(getAPI(jsonData))
	fmt.Println(string(dat))*/

	jr := jsonrespt{}
George Hotz's avatar
George Hotz committed
266
	check(json.NewDecoder(getAPI(jsonData)).Decode(&jr))
267 268 269 270 271 272 273 274
	//fmt.Println(jr.Result)
	blockHeader := jr.Result.ToHeader()

	// put in the start block header
	if startBlock {
		blockHeaderRlp, _ := rlp.EncodeToBytes(blockHeader)
		hash := crypto.Keccak256Hash(blockHeaderRlp)
		preimages[hash] = blockHeaderRlp
George Hotz's avatar
George Hotz committed
275 276 277 278
		emptyHash := common.Hash{}
		if inputs[0] == emptyHash {
			inputs[0] = hash
		}
279 280 281 282 283 284 285 286 287 288 289
		return
	}

	// second block
	if blockHeader.ParentHash != Input(0) {
		fmt.Println(blockHeader.ParentHash, Input(0))
		panic("block transition isn't correct")
	}
	inputs[1] = blockHeader.TxHash
	inputs[2] = blockHeader.Coinbase.Hash()
	inputs[3] = blockHeader.UncleHash
290
	inputs[4] = common.BigToHash(big.NewInt(int64(blockHeader.GasLimit)))
291
	inputs[5] = common.BigToHash(big.NewInt(int64(blockHeader.Time)))
George Hotz's avatar
George Hotz committed
292 293

	// secret input
294
	inputs[6] = blockHeader.Root
George Hotz's avatar
George Hotz committed
295
	inputs[7] = blockHeader.ReceiptHash
George Hotz's avatar
George Hotz committed
296 297 298 299 300 301 302 303

	// save the inputs
	saveinput := make([]byte, 0)
	for i := 0; i < len(inputs); i++ {
		saveinput = append(saveinput, inputs[i].Bytes()[:]...)
	}
	key := fmt.Sprintf("/tmp/eth/%d", blockNumber.Uint64()-1)
	ioutil.WriteFile(key, saveinput, 0644)
304

George Hotz's avatar
George Hotz committed
305
	// save the txs
306 307 308 309
	txs := make([]*types.Transaction, len(jr.Result.Transactions))
	for i := 0; i < len(jr.Result.Transactions); i++ {
		txs[i] = jr.Result.Transactions[i].ToTransaction()
	}
George Hotz's avatar
George Hotz committed
310 311 312 313 314
	testTxHash := types.DeriveSha(types.Transactions(txs), hasher)
	if testTxHash != blockHeader.TxHash {
		fmt.Println(testTxHash, "!=", blockHeader.TxHash)
		panic("tx hash derived wrong")
	}
315

George Hotz's avatar
George Hotz committed
316 317
	// save the uncles
	prefetchUncles(blockHeader.Hash(), blockHeader.UncleHash, hasher)
318
}
319

320
func getProofAccount(blockNumber *big.Int, addr common.Address, skey common.Hash, storage bool) []string {
George Hotz's avatar
George Hotz committed
321 322 323
	addrHash := crypto.Keccak256Hash(addr[:])
	unhashMap[addrHash] = addr

324 325 326 327 328 329 330 331
	r := jsonreq{Jsonrpc: "2.0", Method: "eth_getProof", Id: 1}
	r.Params = make([]interface{}, 3)
	r.Params[0] = addr
	r.Params[1] = [1]common.Hash{skey}
	r.Params[2] = fmt.Sprintf("0x%x", blockNumber.Int64())
	jsonData, _ := json.Marshal(r)
	jr := jsonresp{}
	json.NewDecoder(getAPI(jsonData)).Decode(&jr)
332

333 334 335 336
	if storage {
		return jr.Result.StorageProof[0].Proof
	} else {
		return jr.Result.AccountProof
George Hotz's avatar
George Hotz committed
337
	}
George Hotz's avatar
George Hotz committed
338
}
George Hotz's avatar
George Hotz committed
339

340
func getProvedCodeBytes(blockNumber *big.Int, addrHash common.Hash) []byte {
341
	addr := unhash(addrHash)
George Hotz's avatar
George Hotz committed
342

343 344 345 346 347 348 349
	r := jsonreq{Jsonrpc: "2.0", Method: "eth_getCode", Id: 1}
	r.Params = make([]interface{}, 2)
	r.Params[0] = addr
	r.Params[1] = fmt.Sprintf("0x%x", blockNumber.Int64())
	jsonData, _ := json.Marshal(r)
	jr := jsonresps{}
	json.NewDecoder(getAPI(jsonData)).Decode(&jr)
George Hotz's avatar
George Hotz committed
350

351
	//fmt.Println(jr.Result)
George Hotz's avatar
George Hotz committed
352

353
	// curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getCode","params":["0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", "0x2"],"id":1}'
George Hotz's avatar
George Hotz committed
354

355 356 357
	ret, _ := hex.DecodeString(jr.Result[2:])
	//fmt.Println(ret)
	return ret
George Hotz's avatar
George Hotz committed
358
}