transition.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. // Copyright 2020 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package t8ntool
  17. import (
  18. "crypto/ecdsa"
  19. "encoding/json"
  20. "fmt"
  21. "io/ioutil"
  22. "math/big"
  23. "os"
  24. "path"
  25. "github.com/ethereum/go-ethereum/common"
  26. "github.com/ethereum/go-ethereum/common/hexutil"
  27. "github.com/ethereum/go-ethereum/core"
  28. "github.com/ethereum/go-ethereum/core/state"
  29. "github.com/ethereum/go-ethereum/core/types"
  30. "github.com/ethereum/go-ethereum/core/vm"
  31. "github.com/ethereum/go-ethereum/crypto"
  32. "github.com/ethereum/go-ethereum/log"
  33. "github.com/ethereum/go-ethereum/params"
  34. "github.com/ethereum/go-ethereum/rlp"
  35. "github.com/ethereum/go-ethereum/tests"
  36. "gopkg.in/urfave/cli.v1"
  37. )
  38. const (
  39. ErrorEVM = 2
  40. ErrorVMConfig = 3
  41. ErrorMissingBlockhash = 4
  42. ErrorJson = 10
  43. ErrorIO = 11
  44. stdinSelector = "stdin"
  45. )
  46. type NumberedError struct {
  47. errorCode int
  48. err error
  49. }
  50. func NewError(errorCode int, err error) *NumberedError {
  51. return &NumberedError{errorCode, err}
  52. }
  53. func (n *NumberedError) Error() string {
  54. return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
  55. }
  56. func (n *NumberedError) Code() int {
  57. return n.errorCode
  58. }
  59. type input struct {
  60. Alloc core.GenesisAlloc `json:"alloc,omitempty"`
  61. Env *stEnv `json:"env,omitempty"`
  62. Txs []*txWithKey `json:"txs,omitempty"`
  63. }
  64. func Main(ctx *cli.Context) error {
  65. // Configure the go-ethereum logger
  66. glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
  67. glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
  68. log.Root().SetHandler(glogger)
  69. var (
  70. err error
  71. tracer vm.Tracer
  72. baseDir = ""
  73. )
  74. var getTracer func(txIndex int, txHash common.Hash) (vm.Tracer, error)
  75. // If user specified a basedir, make sure it exists
  76. if ctx.IsSet(OutputBasedir.Name) {
  77. if base := ctx.String(OutputBasedir.Name); len(base) > 0 {
  78. err := os.MkdirAll(base, 0755) // //rw-r--r--
  79. if err != nil {
  80. return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
  81. }
  82. baseDir = base
  83. }
  84. }
  85. if ctx.Bool(TraceFlag.Name) {
  86. // Configure the EVM logger
  87. logConfig := &vm.LogConfig{
  88. DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
  89. DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
  90. DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
  91. Debug: true,
  92. }
  93. var prevFile *os.File
  94. // This one closes the last file
  95. defer func() {
  96. if prevFile != nil {
  97. prevFile.Close()
  98. }
  99. }()
  100. getTracer = func(txIndex int, txHash common.Hash) (vm.Tracer, error) {
  101. if prevFile != nil {
  102. prevFile.Close()
  103. }
  104. traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
  105. if err != nil {
  106. return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
  107. }
  108. prevFile = traceFile
  109. return vm.NewJSONLogger(logConfig, traceFile), nil
  110. }
  111. } else {
  112. getTracer = func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error) {
  113. return nil, nil
  114. }
  115. }
  116. // We need to load three things: alloc, env and transactions. May be either in
  117. // stdin input or in files.
  118. // Check if anything needs to be read from stdin
  119. var (
  120. prestate Prestate
  121. txs types.Transactions // txs to apply
  122. allocStr = ctx.String(InputAllocFlag.Name)
  123. envStr = ctx.String(InputEnvFlag.Name)
  124. txStr = ctx.String(InputTxsFlag.Name)
  125. inputData = &input{}
  126. )
  127. // Figure out the prestate alloc
  128. if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector {
  129. decoder := json.NewDecoder(os.Stdin)
  130. decoder.Decode(inputData)
  131. }
  132. if allocStr != stdinSelector {
  133. inFile, err := os.Open(allocStr)
  134. if err != nil {
  135. return NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err))
  136. }
  137. defer inFile.Close()
  138. decoder := json.NewDecoder(inFile)
  139. if err := decoder.Decode(&inputData.Alloc); err != nil {
  140. return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling alloc-file: %v", err))
  141. }
  142. }
  143. prestate.Pre = inputData.Alloc
  144. // Set the block environment
  145. if envStr != stdinSelector {
  146. inFile, err := os.Open(envStr)
  147. if err != nil {
  148. return NewError(ErrorIO, fmt.Errorf("failed reading env file: %v", err))
  149. }
  150. defer inFile.Close()
  151. decoder := json.NewDecoder(inFile)
  152. var env stEnv
  153. if err := decoder.Decode(&env); err != nil {
  154. return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling env-file: %v", err))
  155. }
  156. inputData.Env = &env
  157. }
  158. prestate.Env = *inputData.Env
  159. vmConfig := vm.Config{
  160. Tracer: tracer,
  161. Debug: (tracer != nil),
  162. }
  163. // Construct the chainconfig
  164. var chainConfig *params.ChainConfig
  165. if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
  166. return NewError(ErrorVMConfig, fmt.Errorf("Failed constructing chain configuration: %v", err))
  167. } else {
  168. chainConfig = cConf
  169. vmConfig.ExtraEips = extraEips
  170. }
  171. // Set the chain id
  172. chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
  173. var txsWithKeys []*txWithKey
  174. if txStr != stdinSelector {
  175. inFile, err := os.Open(txStr)
  176. if err != nil {
  177. return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
  178. }
  179. defer inFile.Close()
  180. decoder := json.NewDecoder(inFile)
  181. if err := decoder.Decode(&txsWithKeys); err != nil {
  182. return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling txs-file: %v", err))
  183. }
  184. } else {
  185. txsWithKeys = inputData.Txs
  186. }
  187. // We may have to sign the transactions.
  188. signer := types.MakeSigner(chainConfig, big.NewInt(int64(prestate.Env.Number)))
  189. if txs, err = signUnsignedTransactions(txsWithKeys, signer); err != nil {
  190. return NewError(ErrorJson, fmt.Errorf("Failed signing transactions: %v", err))
  191. }
  192. // Iterate over all the tests, run them and aggregate the results
  193. // Run the test and aggregate the result
  194. state, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
  195. if err != nil {
  196. return err
  197. }
  198. body, _ := rlp.EncodeToBytes(txs)
  199. // Dump the excution result
  200. collector := make(Alloc)
  201. state.DumpToCollector(collector, false, false, false, nil, -1)
  202. return dispatchOutput(ctx, baseDir, result, collector, body)
  203. }
  204. // txWithKey is a helper-struct, to allow us to use the types.Transaction along with
  205. // a `secretKey`-field, for input
  206. type txWithKey struct {
  207. key *ecdsa.PrivateKey
  208. tx *types.Transaction
  209. }
  210. func (t *txWithKey) UnmarshalJSON(input []byte) error {
  211. // Read the secretKey, if present
  212. type sKey struct {
  213. Key *common.Hash `json:"secretKey"`
  214. }
  215. var key sKey
  216. if err := json.Unmarshal(input, &key); err != nil {
  217. return err
  218. }
  219. if key.Key != nil {
  220. k := key.Key.Hex()[2:]
  221. if ecdsaKey, err := crypto.HexToECDSA(k); err != nil {
  222. return err
  223. } else {
  224. t.key = ecdsaKey
  225. }
  226. }
  227. // Now, read the transaction itself
  228. var tx types.Transaction
  229. if err := json.Unmarshal(input, &tx); err != nil {
  230. return err
  231. }
  232. t.tx = &tx
  233. return nil
  234. }
  235. // signUnsignedTransactions converts the input txs to canonical transactions.
  236. //
  237. // The transactions can have two forms, either
  238. // 1. unsigned or
  239. // 2. signed
  240. // For (1), r, s, v, need so be zero, and the `secretKey` needs to be set.
  241. // If so, we sign it here and now, with the given `secretKey`
  242. // If the condition above is not met, then it's considered a signed transaction.
  243. //
  244. // To manage this, we read the transactions twice, first trying to read the secretKeys,
  245. // and secondly to read them with the standard tx json format
  246. func signUnsignedTransactions(txs []*txWithKey, signer types.Signer) (types.Transactions, error) {
  247. var signedTxs []*types.Transaction
  248. for i, txWithKey := range txs {
  249. tx := txWithKey.tx
  250. key := txWithKey.key
  251. v, r, s := tx.RawSignatureValues()
  252. if key != nil && v.BitLen()+r.BitLen()+s.BitLen() == 0 {
  253. // This transaction needs to be signed
  254. signed, err := types.SignTx(tx, signer, key)
  255. if err != nil {
  256. return nil, NewError(ErrorJson, fmt.Errorf("Tx %d: failed to sign tx: %v", i, err))
  257. }
  258. signedTxs = append(signedTxs, signed)
  259. } else {
  260. // Already signed
  261. signedTxs = append(signedTxs, tx)
  262. }
  263. }
  264. return signedTxs, nil
  265. }
  266. type Alloc map[common.Address]core.GenesisAccount
  267. func (g Alloc) OnRoot(common.Hash) {}
  268. func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
  269. balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
  270. var storage map[common.Hash]common.Hash
  271. if dumpAccount.Storage != nil {
  272. storage = make(map[common.Hash]common.Hash)
  273. for k, v := range dumpAccount.Storage {
  274. storage[k] = common.HexToHash(v)
  275. }
  276. }
  277. genesisAccount := core.GenesisAccount{
  278. Code: common.FromHex(dumpAccount.Code),
  279. Storage: storage,
  280. Balance: balance,
  281. Nonce: dumpAccount.Nonce,
  282. }
  283. g[addr] = genesisAccount
  284. }
  285. // saveFile marshalls the object to the given file
  286. func saveFile(baseDir, filename string, data interface{}) error {
  287. b, err := json.MarshalIndent(data, "", " ")
  288. if err != nil {
  289. return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
  290. }
  291. location := path.Join(baseDir, filename)
  292. if err = ioutil.WriteFile(location, b, 0644); err != nil {
  293. return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
  294. }
  295. log.Info("Wrote file", "file", location)
  296. return nil
  297. }
  298. // dispatchOutput writes the output data to either stderr or stdout, or to the specified
  299. // files
  300. func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc, body hexutil.Bytes) error {
  301. stdOutObject := make(map[string]interface{})
  302. stdErrObject := make(map[string]interface{})
  303. dispatch := func(baseDir, fName, name string, obj interface{}) error {
  304. switch fName {
  305. case "stdout":
  306. stdOutObject[name] = obj
  307. case "stderr":
  308. stdErrObject[name] = obj
  309. case "":
  310. // don't save
  311. default: // save to file
  312. if err := saveFile(baseDir, fName, obj); err != nil {
  313. return err
  314. }
  315. }
  316. return nil
  317. }
  318. if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
  319. return err
  320. }
  321. if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
  322. return err
  323. }
  324. if err := dispatch(baseDir, ctx.String(OutputBodyFlag.Name), "body", body); err != nil {
  325. return err
  326. }
  327. if len(stdOutObject) > 0 {
  328. b, err := json.MarshalIndent(stdOutObject, "", " ")
  329. if err != nil {
  330. return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
  331. }
  332. os.Stdout.Write(b)
  333. }
  334. if len(stdErrObject) > 0 {
  335. b, err := json.MarshalIndent(stdErrObject, "", " ")
  336. if err != nil {
  337. return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
  338. }
  339. os.Stderr.Write(b)
  340. }
  341. return nil
  342. }