diff --git a/README.md b/README.md
index 39d5fcd21f..52bbe0a1a7 100644
--- a/README.md
+++ b/README.md
@@ -136,16 +136,16 @@ you'd expect.
HTTP based JSON-RPC API options:
- * `--rpc` Enable the HTTP-RPC server
- * `--rpcaddr` HTTP-RPC server listening interface (default: `localhost`)
- * `--rpcport` HTTP-RPC server listening port (default: `8545`)
- * `--rpcapi` API's offered over the HTTP-RPC interface (default: `eth,net,web3`)
- * `--rpccorsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced)
+ * `--http` Enable the HTTP-RPC server
+ * `--http.addr` HTTP-RPC server listening interface (default: `localhost`)
+ * `--http.port` HTTP-RPC server listening port (default: `8545`)
+ * `--http.api` API's offered over the HTTP-RPC interface (default: `eth,net,web3`)
+ * `--http.corsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced)
* `--ws` Enable the WS-RPC server
- * `--wsaddr` WS-RPC server listening interface (default: `localhost`)
- * `--wsport` WS-RPC server listening port (default: `8546`)
- * `--wsapi` API's offered over the WS-RPC interface (default: `eth,net,web3`)
- * `--wsorigins` Origins from which to accept websockets requests
+ * `--ws.addr` WS-RPC server listening interface (default: `localhost`)
+ * `--ws.port` WS-RPC server listening port (default: `8546`)
+ * `--ws.api` API's offered over the WS-RPC interface (default: `eth,net,web3`)
+ * `--ws.origins` Origins from which to accept websockets requests
* `--ipcdisable` Disable the IPC-RPC server
* `--ipcapi` API's offered over the IPC-RPC interface (default: `admin,debug,eth,miner,net,personal,shh,txpool,web3`)
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
diff --git a/accounts/abi/error.go b/accounts/abi/error.go
index 8d61d574c5..b63a215ad9 100644
--- a/accounts/abi/error.go
+++ b/accounts/abi/error.go
@@ -39,11 +39,11 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
// type in t.
func sliceTypeCheck(t Type, val reflect.Value) error {
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
- return typeErr(formatSliceString(t.getType().Kind(), t.Size), val.Type())
+ return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
}
if t.T == ArrayTy && val.Len() != t.Size {
- return typeErr(formatSliceString(t.Elem.getType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
+ return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
}
if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
@@ -52,8 +52,8 @@ func sliceTypeCheck(t Type, val reflect.Value) error {
}
}
- if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.getType().Kind() {
- return typeErr(formatSliceString(t.Elem.getType().Kind(), t.Size), val.Type())
+ if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.GetType().Kind() {
+ return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
}
return nil
}
@@ -66,10 +66,10 @@ func typeCheck(t Type, value reflect.Value) error {
}
// Check base type validity. Element types will be checked later on.
- if t.getType().Kind() != value.Kind() {
- return typeErr(t.getType().Kind(), value.Kind())
+ if t.GetType().Kind() != value.Kind() {
+ return typeErr(t.GetType().Kind(), value.Kind())
} else if t.T == FixedBytesTy && t.Size != value.Len() {
- return typeErr(t.getType(), value.Type())
+ return typeErr(t.GetType(), value.Type())
} else {
return nil
}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 8d73b49fed..4769bd984f 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -176,7 +176,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
overloadedNames[fieldName] = fieldName
fields = append(fields, reflect.StructField{
Name: fieldName, // reflect.StructOf will panic for any exported field.
- Type: cType.getType(),
+ Type: cType.GetType(),
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
})
elems = append(elems, &cType)
@@ -214,7 +214,8 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
return
}
-func (t Type) getType() reflect.Type {
+// GetType returns the reflection type of the ABI type.
+func (t Type) GetType() reflect.Type {
switch t.T {
case IntTy:
return reflectIntType(false, t.Size)
@@ -225,9 +226,9 @@ func (t Type) getType() reflect.Type {
case StringTy:
return reflect.TypeOf("")
case SliceTy:
- return reflect.SliceOf(t.Elem.getType())
+ return reflect.SliceOf(t.Elem.GetType())
case ArrayTy:
- return reflect.ArrayOf(t.Size, t.Elem.getType())
+ return reflect.ArrayOf(t.Size, t.Elem.GetType())
case TupleTy:
return t.TupleType
case AddressTy:
diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
index 2f90742139..0348ad24d7 100644
--- a/accounts/abi/unpack.go
+++ b/accounts/abi/unpack.go
@@ -110,7 +110,7 @@ func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
}
// convert
- array := reflect.New(t.getType()).Elem()
+ array := reflect.New(t.GetType()).Elem()
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
return array.Interface(), nil
@@ -131,10 +131,10 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
if t.T == SliceTy {
// declare our slice
- refSlice = reflect.MakeSlice(t.getType(), size, size)
+ refSlice = reflect.MakeSlice(t.GetType(), size, size)
} else if t.T == ArrayTy {
// declare our array
- refSlice = reflect.New(t.getType()).Elem()
+ refSlice = reflect.New(t.GetType()).Elem()
} else {
return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
}
@@ -158,7 +158,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
}
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
- retval := reflect.New(t.getType()).Elem()
+ retval := reflect.New(t.GetType()).Elem()
virtualArgs := 0
for index, elem := range t.TupleElems {
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
diff --git a/accounts/external/backend.go b/accounts/external/backend.go
index 0ebb073009..949333183f 100644
--- a/accounts/external/backend.go
+++ b/accounts/external/backend.go
@@ -29,7 +29,6 @@ import (
"github.com/celo-org/celo-blockchain/core/types"
blscrypto "github.com/celo-org/celo-blockchain/crypto/bls"
"github.com/celo-org/celo-blockchain/event"
- "github.com/celo-org/celo-blockchain/internal/ethapi"
"github.com/celo-org/celo-blockchain/log"
"github.com/celo-org/celo-blockchain/rpc"
"github.com/celo-org/celo-blockchain/signer/core"
@@ -196,8 +195,13 @@ func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]by
return signature, nil
}
+// signTransactionResult represents the signinig result returned by clef.
+type signTransactionResult struct {
+ Raw hexutil.Bytes `json:"raw"`
+ Tx *types.Transaction `json:"tx"`
+}
+
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
- res := ethapi.SignTransactionResult{}
data := hexutil.Bytes(tx.Data())
var to *common.MixedcaseAddress
if tx.To() != nil {
@@ -213,6 +217,7 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
To: to,
From: common.NewMixedcaseAddress(account.Address),
}
+ var res signTransactionResult
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
return nil, err
}
diff --git a/accounts/keystore/keystore.go b/accounts/keystore/keystore.go
index b325854564..47a065fd9a 100644
--- a/accounts/keystore/keystore.go
+++ b/accounts/keystore/keystore.go
@@ -54,7 +54,7 @@ var (
// ErrAccountAlreadyExists is returned if an account attempted to import is
// already present in the keystore.
- ErrAccountAlreadyExists = errors.New("account alreaady exists")
+ ErrAccountAlreadyExists = errors.New("account already exists")
)
// KeyStoreType is the reflect type of a keystore backend.
diff --git a/build/ci.go b/build/ci.go
index d8b2257f76..54d603b9ad 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -1215,6 +1215,8 @@ func doPurge(cmdline []string) {
if err != nil {
log.Fatal(err)
}
+ fmt.Printf("Found %d blobs\n", len(blobs))
+
// Iterate over the blobs, collect and sort all unstable builds
for i := 0; i < len(blobs); i++ {
if !strings.Contains(blobs[i].Name, "unstable") {
@@ -1236,6 +1238,7 @@ func doPurge(cmdline []string) {
break
}
}
+ fmt.Printf("Deleting %d blobs\n", len(blobs))
// Delete all marked as such and return
if err := build.AzureBlobstoreDelete(auth, blobs); err != nil {
log.Fatal(err)
diff --git a/cmd/clef/README.md b/cmd/clef/README.md
index 5cd3da8ab1..029d67c8fd 100644
--- a/cmd/clef/README.md
+++ b/cmd/clef/README.md
@@ -114,9 +114,9 @@ Some snags and todos
Clef listens to HTTP requests on `rpcaddr`:`rpcport` (or to IPC on `ipcpath`), with the same JSON-RPC standard as Geth. The messages are expected to be [JSON-RPC 2.0 standard](https://www.jsonrpc.org/specification).
-Some of these call can require user interaction. Clients must be aware that responses may be delayed significantly or may never be received if a users decides to ignore the confirmation request.
+Some of these calls can require user interaction. Clients must be aware that responses may be delayed significantly or may never be received if a user decides to ignore the confirmation request.
-The External API is **untrusted**: it does not accept credentials over this API, nor does it expect that requests have any authority.
+The External API is **untrusted**: it does not accept credentials, nor does it expect that requests have any authority.
### Internal UI API
@@ -145,13 +145,11 @@ See the [external API changelog](extapi_changelog.md) for information about chan
All hex encoded values must be prefixed with `0x`.
-## Methods
-
### account_new
#### Create new password protected account
-The signer will generate a new private key, encrypts it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and stores it in the keystore directory.
+The signer will generate a new private key, encrypt it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and store it in the keystore directory.
The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts.
#### Arguments
@@ -160,7 +158,6 @@ None
#### Result
- address [string]: account address that is derived from the generated key
- - url [string]: location of the keyfile
#### Sample call
```json
@@ -172,14 +169,11 @@ None
}
```
Response
-```
+```json
{
"id": 0,
"jsonrpc": "2.0",
- "result": {
- "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
- "url": "keystore:///my/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
- }
+ "result": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133"
}
```
@@ -195,8 +189,6 @@ None
#### Result
- array with account records:
- account.address [string]: account address that is derived from the generated key
- - account.type [string]: type of the
- - account.url [string]: location of the account
#### Sample call
```json
@@ -207,21 +199,13 @@ None
}
```
Response
-```
+```json
{
"id": 1,
"jsonrpc": "2.0",
"result": [
- {
- "address": "0xafb2f771f58513609765698f65d3f2f0224a956f",
- "type": "account",
- "url": "keystore:///tmp/keystore/UTC--2017-08-24T07-26-47.162109726Z--afb2f771f58513609765698f65d3f2f0224a956f"
- },
- {
- "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
- "type": "account",
- "url": "keystore:///tmp/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
- }
+ "0xafb2f771f58513609765698f65d3f2f0224a956f",
+ "0xbea9183f8f4f03d427f6bcea17388bdff1cab133"
]
}
```
@@ -229,10 +213,10 @@ Response
### account_signTransaction
#### Sign transactions
- Signs a transactions and responds with the signed transaction in RLP encoded form.
+ Signs a transaction and responds with the signed transaction in RLP-encoded and JSON forms.
#### Arguments
- 2. transaction object:
+ 1. transaction object:
- `from` [address]: account to send the transaction from
- `to` [address]: receiver account. If omitted or `0x`, will cause contract creation.
- `gas` [number]: maximum amount of gas to burn
@@ -240,12 +224,13 @@ Response
- `value` [number:optional]: amount of Wei to send with the transaction
- `data` [data:optional]: input data
- `nonce` [number]: account nonce
- 3. method signature [string:optional]
+ 1. method signature [string:optional]
- The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected.
#### Result
- - signed transaction in RLP encoded form [data]
+ - raw [data]: signed transaction in RLP encoded form
+ - tx [json]: signed transaction in JSON form
#### Sample call
```json
@@ -270,11 +255,22 @@ Response
```json
{
- "id": 2,
"jsonrpc": "2.0",
- "error": {
- "code": -32000,
- "message": "Request denied"
+ "id": 2,
+ "result": {
+ "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
+ "tx": {
+ "nonce": "0x0",
+ "gasPrice": "0x1234",
+ "gas": "0x55555",
+ "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
+ "value": "0x1234",
+ "input": "0xabcd",
+ "v": "0x26",
+ "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
+ "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
+ "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
+ }
}
}
```
@@ -326,7 +322,7 @@ Response
Bash example:
```bash
-#curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
+> curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
{"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}}
```
@@ -372,7 +368,7 @@ Response
### account_signTypedData
#### Sign data
- Signs a chunk of structured data conformant to [EIP712]([EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md)) and returns the calculated signature.
+ Signs a chunk of structured data conformant to [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and returns the calculated signature.
#### Arguments
- account [address]: account to sign with
@@ -468,7 +464,7 @@ Response
### account_ecRecover
-#### Sign data
+#### Recover the signing address
Derive the address from the account that was used to sign data with content type `text/plain` and the signature.
@@ -486,7 +482,6 @@ Derive the address from the account that was used to sign data with content type
"jsonrpc": "2.0",
"method": "account_ecRecover",
"params": [
- "data/plain",
"0xaabbccdd",
"0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
]
@@ -502,117 +497,36 @@ Response
}
```
-### account_import
+### account_version
-#### Import account
- Import a private key into the keystore. The imported key is expected to be encrypted according to the web3 keystore
- format.
-
-#### Arguments
- - account [object]: key in [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) (retrieved with account_export)
+#### Get external API version
-#### Result
- - imported key [object]:
- - key.address [address]: address of the imported key
- - key.type [string]: type of the account
- - key.url [string]: key URL
-
-#### Sample call
-```json
-{
- "id": 6,
- "jsonrpc": "2.0",
- "method": "account_import",
- "params": [
- {
- "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
- "crypto": {
- "cipher": "aes-128-ctr",
- "cipherparams": {
- "iv": "401c39a7c7af0388491c3d3ecb39f532"
- },
- "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
- "kdf": "scrypt",
- "kdfparams": {
- "dklen": 32,
- "n": 262144,
- "p": 1,
- "r": 8,
- "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
- },
- "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
- },
- "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
- "version": 3
- }
- ]
-}
-```
-Response
-
-```json
-{
- "id": 6,
- "jsonrpc": "2.0",
- "result": {
- "address": "0xc7412fc59930fd90099c917a50e5f11d0934b2f5",
- "type": "account",
- "url": "keystore:///tmp/keystore/UTC--2017-08-24T11-00-42.032024108Z--c7412fc59930fd90099c917a50e5f11d0934b2f5"
- }
-}
-```
-
-### account_export
-
-#### Export account from keystore
- Export a private key from the keystore. The exported private key is encrypted with the original password. When the
- key is imported later this password is required.
+Get the version of the external API used by Clef.
#### Arguments
- - account [address]: export private key that is associated with this account
+
+None
#### Result
- - exported key, see [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) for
- more information
+
+* external API version [string]
#### Sample call
```json
{
- "id": 5,
+ "id": 0,
"jsonrpc": "2.0",
- "method": "account_export",
- "params": [
- "0xc7412fc59930fd90099c917a50e5f11d0934b2f5"
- ]
+ "method": "account_version",
+ "params": []
}
```
-Response
+Response
```json
{
- "id": 5,
- "jsonrpc": "2.0",
- "result": {
- "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
- "crypto": {
- "cipher": "aes-128-ctr",
- "cipherparams": {
- "iv": "401c39a7c7af0388491c3d3ecb39f532"
- },
- "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
- "kdf": "scrypt",
- "kdfparams": {
- "dklen": 32,
- "n": 262144,
- "p": 1,
- "r": 8,
- "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
- },
- "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
- },
- "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
- "version": 3
- }
+ "id": 0,
+ "jsonrpc": "2.0",
+ "result": "6.0.0"
}
```
@@ -624,7 +538,7 @@ By starting the signer with the switch `--stdio-ui-test`, the signer will invoke
denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'.
-All methods in this API uses object-based parameters, so that there can be no mixups of parameters: each piece of data is accessed by key.
+All methods in this API use object-based parameters, so that there can be no mixup of parameters: each piece of data is accessed by key.
See the [ui API changelog](intapi_changelog.md) for information about changes to this API.
@@ -783,12 +697,10 @@ Invoked when a request for account listing has been made.
{
"accounts": [
{
- "type": "Account",
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42",
"address": "0x123409812340981234098123409812deadbeef42"
},
{
- "type": "Account",
"url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42",
"address": "0xcafebabedeadbeef34098123409812deadbeef42"
}
@@ -818,7 +730,13 @@ Invoked when a request for account listing has been made.
{
"address": "0x123409812340981234098123409812deadbeef42",
"raw_data": "0x01020304",
- "message": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004",
+ "messages": [
+ {
+ "name": "message",
+ "value": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004",
+ "type": "text/plain"
+ }
+ ],
"hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310",
"meta": {
"remote": "signer binary",
@@ -828,12 +746,34 @@ Invoked when a request for account listing has been made.
}
]
}
+```
+
+### ApproveNewAccount / `ui_approveNewAccount`
+
+Invoked when a request for creating a new account has been made.
+#### Sample call
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 4,
+ "method": "ui_approveNewAccount",
+ "params": [
+ {
+ "meta": {
+ "remote": "signer binary",
+ "local": "main",
+ "scheme": "in-proc"
+ }
+ }
+ ]
+}
```
### ShowInfo / `ui_showInfo`
-The UI should show the info to the user. Does not expect response.
+The UI should show the info (a single message) to the user. Does not expect response.
#### Sample call
@@ -843,9 +783,7 @@ The UI should show the info to the user. Does not expect response.
"id": 9,
"method": "ui_showInfo",
"params": [
- {
- "text": "Tests completed"
- }
+ "Tests completed"
]
}
@@ -853,18 +791,16 @@ The UI should show the info to the user. Does not expect response.
### ShowError / `ui_showError`
-The UI should show the info to the user. Does not expect response.
+The UI should show the error (a single message) to the user. Does not expect response.
```json
{
"jsonrpc": "2.0",
"id": 2,
- "method": "ShowError",
+ "method": "ui_showError",
"params": [
- {
- "text": "Testing 'ShowError'"
- }
+ "Something bad happened!"
]
}
@@ -878,9 +814,36 @@ When implementing rate-limited rules, this callback should be used.
TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
+Example call:
+```json
+
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "ui_onApprovedTx",
+ "params": [
+ {
+ "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
+ "tx": {
+ "nonce": "0x0",
+ "gasPrice": "0x1",
+ "gas": "0x333",
+ "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
+ "value": "0x0",
+ "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
+ "v": "0x26",
+ "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
+ "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
+ "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
+ }
+ }
+ ]
+}
+```
+
### OnSignerStartup / `ui_onSignerStartup`
-This method provide the UI with information about what API version the signer uses (both internal and external) as well as build-info and external API,
+This method provides the UI with information about what API version the signer uses (both internal and external) as well as build-info and external API,
in k/v-form.
Example call:
@@ -904,6 +867,27 @@ Example call:
```
+### OnInputRequired / `ui_onInputRequired`
+
+Invoked when Clef requires user input (e.g. a password).
+
+Example call:
+```json
+
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "ui_onInputRequired",
+ "params": [
+ {
+ "title": "Account password",
+ "prompt": "Please enter the password for account 0x694267f14675d7e1b9494fd8d72fefe1755710fa",
+ "isPassword": true
+ }
+ ]
+}
+```
+
### Rules for UI apis
diff --git a/cmd/clef/main.go b/cmd/clef/main.go
index ff5b5ca8a6..66e9877daf 100644
--- a/cmd/clef/main.go
+++ b/cmd/clef/main.go
@@ -40,7 +40,6 @@ import (
"github.com/celo-org/celo-blockchain/cmd/utils"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/common/hexutil"
- "github.com/celo-org/celo-blockchain/console/prompt"
"github.com/celo-org/celo-blockchain/core/types"
"github.com/celo-org/celo-blockchain/crypto"
"github.com/celo-org/celo-blockchain/internal/ethapi"
@@ -289,7 +288,7 @@ func initializeSecrets(c *cli.Context) error {
text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!"
var password string
for {
- password = getPassPhrase(text, true)
+ password = utils.GetPassPhrase(text, true)
if err := core.ValidatePasswordFormat(password); err != nil {
fmt.Printf("invalid password: %v\n", err)
} else {
@@ -362,7 +361,7 @@ func setCredential(ctx *cli.Context) error {
utils.Fatalf("Invalid address specified: %s", addr)
}
address := common.HexToAddress(addr)
- password := getPassPhrase("Please enter a password to store for this address:", true)
+ password := utils.GetPassPhrase("Please enter a password to store for this address:", true)
fmt.Println()
stretchedKey, err := readMasterKey(ctx, nil)
@@ -719,7 +718,7 @@ func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
}
password = resp.Text
} else {
- password = getPassPhrase("Decrypt master seed of clef", false)
+ password = utils.GetPassPhrase("Decrypt master seed of clef", false)
}
masterSeed, err := decryptSeed(cipherKey, password)
if err != nil {
@@ -888,27 +887,6 @@ func testExternalUI(api *core.SignerAPI) {
}
-// getPassPhrase retrieves the password associated with clef, either fetched
-// from a list of preloaded passphrases, or requested interactively from the user.
-// TODO: there are many `getPassPhrase` functions, it will be better to abstract them into one.
-func getPassPhrase(query string, confirmation bool) string {
- fmt.Println(query)
- password, err := prompt.Stdin.PromptPassword("Password: ")
- if err != nil {
- utils.Fatalf("Failed to read password: %v", err)
- }
- if confirmation {
- confirm, err := prompt.Stdin.PromptPassword("Repeat password: ")
- if err != nil {
- utils.Fatalf("Failed to read password confirmation: %v", err)
- }
- if password != confirm {
- utils.Fatalf("Passwords do not match")
- }
- }
- return password
-}
-
type encryptedSeedStorage struct {
Description string `json:"description"`
Version int `json:"version"`
diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go
index 14f8328237..96951a198a 100644
--- a/cmd/devp2p/discv4cmd.go
+++ b/cmd/devp2p/discv4cmd.go
@@ -19,12 +19,15 @@ package main
import (
"fmt"
"net"
+ "os"
"strings"
"time"
+ "github.com/celo-org/celo-blockchain/cmd/devp2p/internal/v4test"
"github.com/celo-org/celo-blockchain/cmd/utils"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/crypto"
+ "github.com/celo-org/celo-blockchain/internal/utesting"
"github.com/celo-org/celo-blockchain/node"
"github.com/celo-org/celo-blockchain/p2p/discover"
"github.com/celo-org/celo-blockchain/p2p/enode"
@@ -43,6 +46,7 @@ var (
discv4ResolveCommand,
discv4ResolveJSONCommand,
discv4CrawlCommand,
+ discv4TestCommand,
},
}
discv4PingCommand = cli.Command{
@@ -77,6 +81,12 @@ var (
Action: discv4Crawl,
Flags: []cli.Flag{bootnodesFlag, crawlTimeoutFlag},
}
+ discv4TestCommand = cli.Command{
+ Name: "test",
+ Usage: "Runs tests against a node",
+ Action: discv4Test,
+ Flags: []cli.Flag{remoteEnodeFlag, testPatternFlag, testListen1Flag, testListen2Flag},
+ }
)
var (
@@ -101,6 +111,25 @@ var (
Usage: "Time limit for the crawl.",
Value: 30 * time.Minute,
}
+ remoteEnodeFlag = cli.StringFlag{
+ Name: "remote",
+ Usage: "Enode of the remote node under test",
+ EnvVar: "REMOTE_ENODE",
+ }
+ testPatternFlag = cli.StringFlag{
+ Name: "run",
+ Usage: "Pattern of test suite(s) to run",
+ }
+ testListen1Flag = cli.StringFlag{
+ Name: "listen1",
+ Usage: "IP address of the first tester",
+ Value: v4test.Listen1,
+ }
+ testListen2Flag = cli.StringFlag{
+ Name: "listen2",
+ Usage: "IP address of the second tester",
+ Value: v4test.Listen2,
+ }
)
var networkIdFlag = cli.Uint64Flag{
@@ -193,6 +222,28 @@ func discv4Crawl(ctx *cli.Context) error {
return nil
}
+func discv4Test(ctx *cli.Context) error {
+ // Configure test package globals.
+ if !ctx.IsSet(remoteEnodeFlag.Name) {
+ return fmt.Errorf("Missing -%v", remoteEnodeFlag.Name)
+ }
+ v4test.Remote = ctx.String(remoteEnodeFlag.Name)
+ v4test.Listen1 = ctx.String(testListen1Flag.Name)
+ v4test.Listen2 = ctx.String(testListen2Flag.Name)
+
+ // Filter and run test cases.
+ tests := v4test.AllTests
+ if ctx.IsSet(testPatternFlag.Name) {
+ tests = utesting.MatchTests(tests, ctx.String(testPatternFlag.Name))
+ }
+ results := utesting.RunTests(tests, os.Stdout)
+ if fails := utesting.CountFailures(results); fails > 0 {
+ return fmt.Errorf("%v/%v tests passed.", len(tests)-fails, len(tests))
+ }
+ fmt.Printf("%v/%v passed\n", len(tests), len(tests))
+ return nil
+}
+
// startV4 starts an ephemeral discovery V4 node.
func startV4(ctx *cli.Context) *discover.UDPv4 {
ln, config := makeDiscoveryConfig(ctx)
diff --git a/cmd/devp2p/internal/v4test/discv4tests.go b/cmd/devp2p/internal/v4test/discv4tests.go
new file mode 100644
index 0000000000..eee4c56fd1
--- /dev/null
+++ b/cmd/devp2p/internal/v4test/discv4tests.go
@@ -0,0 +1,467 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package v4test
+
+import (
+ "bytes"
+ "crypto/rand"
+ "fmt"
+ "net"
+ "reflect"
+ "time"
+
+ "github.com/celo-org/celo-blockchain/crypto"
+ "github.com/celo-org/celo-blockchain/internal/utesting"
+ "github.com/celo-org/celo-blockchain/p2p/discover/v4wire"
+)
+
+const (
+ expiration = 20 * time.Second
+ wrongPacket = 66
+ macSize = 256 / 8
+)
+
+var (
+ // Remote node under test
+ Remote string
+ // IP where the first tester is listening, port will be assigned
+ Listen1 string = "127.0.0.1"
+ // IP where the second tester is listening, port will be assigned
+ // Before running the test, you may have to `sudo ifconfig lo0 add 127.0.0.2` (on MacOS at least)
+ Listen2 string = "127.0.0.2"
+)
+
+type pingWithJunk struct {
+ Version uint
+ From, To v4wire.Endpoint
+ Expiration uint64
+ JunkData1 uint
+ JunkData2 []byte
+}
+
+func (req *pingWithJunk) Name() string { return "PING/v4" }
+func (req *pingWithJunk) Kind() byte { return v4wire.PingPacket }
+
+type pingWrongType struct {
+ Version uint
+ From, To v4wire.Endpoint
+ Expiration uint64
+}
+
+func (req *pingWrongType) Name() string { return "WRONG/v4" }
+func (req *pingWrongType) Kind() byte { return wrongPacket }
+
+func futureExpiration() uint64 {
+ return uint64(time.Now().Add(expiration).Unix())
+}
+
+// This test just sends a PING packet and expects a response.
+func BasicPing(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ pingHash := te.send(te.l1, &v4wire.Ping{
+ Version: 4,
+ From: te.localEndpoint(te.l1),
+ To: te.remoteEndpoint(),
+ Expiration: futureExpiration(),
+ })
+
+ reply, _, _ := te.read(te.l1)
+ if err := te.checkPong(reply, pingHash); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// checkPong verifies that reply is a valid PONG matching the given ping hash.
+func (te *testenv) checkPong(reply v4wire.Packet, pingHash []byte) error {
+ if reply == nil || reply.Kind() != v4wire.PongPacket {
+ return fmt.Errorf("expected PONG reply, got %v", reply)
+ }
+ pong := reply.(*v4wire.Pong)
+ if !bytes.Equal(pong.ReplyTok, pingHash) {
+ return fmt.Errorf("PONG reply token mismatch: got %x, want %x", pong.ReplyTok, pingHash)
+ }
+ wantEndpoint := te.localEndpoint(te.l1)
+ if !reflect.DeepEqual(pong.To, wantEndpoint) {
+ return fmt.Errorf("PONG 'to' endpoint mismatch: got %+v, want %+v", pong.To, wantEndpoint)
+ }
+ if v4wire.Expired(pong.Expiration) {
+ return fmt.Errorf("PONG is expired (%v)", pong.Expiration)
+ }
+ return nil
+}
+
+// This test sends a PING packet with wrong 'to' field and expects a PONG response.
+func PingWrongTo(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ wrongEndpoint := v4wire.Endpoint{IP: net.ParseIP("192.0.2.0")}
+ pingHash := te.send(te.l1, &v4wire.Ping{
+ Version: 4,
+ From: te.localEndpoint(te.l1),
+ To: wrongEndpoint,
+ Expiration: futureExpiration(),
+ })
+
+ reply, _, _ := te.read(te.l1)
+ if err := te.checkPong(reply, pingHash); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// This test sends a PING packet with wrong 'from' field and expects a PONG response.
+func PingWrongFrom(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ wrongEndpoint := v4wire.Endpoint{IP: net.ParseIP("192.0.2.0")}
+ pingHash := te.send(te.l1, &v4wire.Ping{
+ Version: 4,
+ From: wrongEndpoint,
+ To: te.remoteEndpoint(),
+ Expiration: futureExpiration(),
+ })
+
+ reply, _, _ := te.read(te.l1)
+ if err := te.checkPong(reply, pingHash); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// This test sends a PING packet with additional data at the end and expects a PONG
+// response. The remote node should respond because EIP-8 mandates ignoring additional
+// trailing data.
+func PingExtraData(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ pingHash := te.send(te.l1, &pingWithJunk{
+ Version: 4,
+ From: te.localEndpoint(te.l1),
+ To: te.remoteEndpoint(),
+ Expiration: futureExpiration(),
+ JunkData1: 42,
+ JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
+ })
+
+ reply, _, _ := te.read(te.l1)
+ if err := te.checkPong(reply, pingHash); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// This test sends a PING packet with additional data and wrong 'from' field
+// and expects a PONG response.
+func PingExtraDataWrongFrom(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ wrongEndpoint := v4wire.Endpoint{IP: net.ParseIP("192.0.2.0")}
+ req := pingWithJunk{
+ Version: 4,
+ From: wrongEndpoint,
+ To: te.remoteEndpoint(),
+ Expiration: futureExpiration(),
+ JunkData1: 42,
+ JunkData2: []byte{9, 8, 7, 6, 5, 4, 3, 2, 1},
+ }
+ pingHash := te.send(te.l1, &req)
+ reply, _, _ := te.read(te.l1)
+ if err := te.checkPong(reply, pingHash); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// This test sends a PING packet with an expiration in the past.
+// The remote node should not respond.
+func PingPastExpiration(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ te.send(te.l1, &v4wire.Ping{
+ Version: 4,
+ From: te.localEndpoint(te.l1),
+ To: te.remoteEndpoint(),
+ Expiration: -futureExpiration(),
+ })
+
+ reply, _, _ := te.read(te.l1)
+ if reply != nil {
+ t.Fatal("Expected no reply, got", reply)
+ }
+}
+
+// This test sends an invalid packet. The remote node should not respond.
+func WrongPacketType(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ te.send(te.l1, &pingWrongType{
+ Version: 4,
+ From: te.localEndpoint(te.l1),
+ To: te.remoteEndpoint(),
+ Expiration: futureExpiration(),
+ })
+
+ reply, _, _ := te.read(te.l1)
+ if reply != nil {
+ t.Fatal("Expected no reply, got", reply)
+ }
+}
+
+// This test verifies that the default behaviour of ignoring 'from' fields is unaffected by
+// the bonding process. After bonding, it pings the target with a different from endpoint.
+func BondThenPingWithWrongFrom(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+ bond(t, te)
+
+ wrongEndpoint := v4wire.Endpoint{IP: net.ParseIP("192.0.2.0")}
+ pingHash := te.send(te.l1, &v4wire.Ping{
+ Version: 4,
+ From: wrongEndpoint,
+ To: te.remoteEndpoint(),
+ Expiration: futureExpiration(),
+ })
+
+ reply, _, _ := te.read(te.l1)
+ if err := te.checkPong(reply, pingHash); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// This test just sends FINDNODE. The remote node should not reply
+// because the endpoint proof has not completed.
+func FindnodeWithoutEndpointProof(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ req := v4wire.Findnode{Expiration: futureExpiration()}
+ rand.Read(req.Target[:])
+ te.send(te.l1, &req)
+
+ reply, _, _ := te.read(te.l1)
+ if reply != nil {
+ t.Fatal("Expected no response, got", reply)
+ }
+}
+
+// BasicFindnode sends a FINDNODE request after performing the endpoint
+// proof. The remote node should respond.
+func BasicFindnode(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+ bond(t, te)
+
+ findnode := v4wire.Findnode{Expiration: futureExpiration()}
+ rand.Read(findnode.Target[:])
+ te.send(te.l1, &findnode)
+
+ reply, _, err := te.read(te.l1)
+ if err != nil {
+ t.Fatal("read find nodes", err)
+ }
+ if reply.Kind() != v4wire.NeighborsPacket {
+ t.Fatal("Expected neighbors, got", reply.Name())
+ }
+}
+
+// This test sends an unsolicited NEIGHBORS packet after the endpoint proof, then sends
+// FINDNODE to read the remote table. The remote node should not return the node contained
+// in the unsolicited NEIGHBORS packet.
+func UnsolicitedNeighbors(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+ bond(t, te)
+
+ // Send unsolicited NEIGHBORS response.
+ fakeKey, _ := crypto.GenerateKey()
+ encFakeKey := v4wire.EncodePubkey(&fakeKey.PublicKey)
+ neighbors := v4wire.Neighbors{
+ Expiration: futureExpiration(),
+ Nodes: []v4wire.Node{{
+ ID: encFakeKey,
+ IP: net.IP{1, 2, 3, 4},
+ UDP: 30303,
+ TCP: 30303,
+ }},
+ }
+ te.send(te.l1, &neighbors)
+
+ // Check if the remote node included the fake node.
+ te.send(te.l1, &v4wire.Findnode{
+ Expiration: futureExpiration(),
+ Target: encFakeKey,
+ })
+
+ reply, _, err := te.read(te.l1)
+ if err != nil {
+ t.Fatal("read find nodes", err)
+ }
+ if reply.Kind() != v4wire.NeighborsPacket {
+ t.Fatal("Expected neighbors, got", reply.Name())
+ }
+ nodes := reply.(*v4wire.Neighbors).Nodes
+ if contains(nodes, encFakeKey) {
+ t.Fatal("neighbors response contains node from earlier unsolicited neighbors response")
+ }
+}
+
+// This test sends FINDNODE with an expiration timestamp in the past.
+// The remote node should not respond.
+func FindnodePastExpiration(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+ bond(t, te)
+
+ findnode := v4wire.Findnode{Expiration: -futureExpiration()}
+ rand.Read(findnode.Target[:])
+ te.send(te.l1, &findnode)
+
+ for {
+ reply, _, _ := te.read(te.l1)
+ if reply == nil {
+ return
+ } else if reply.Kind() == v4wire.NeighborsPacket {
+ t.Fatal("Unexpected NEIGHBORS response for expired FINDNODE request")
+ }
+ }
+}
+
+// bond performs the endpoint proof with the remote node.
+func bond(t *utesting.T, te *testenv) {
+ te.send(te.l1, &v4wire.Ping{
+ Version: 4,
+ From: te.localEndpoint(te.l1),
+ To: te.remoteEndpoint(),
+ Expiration: futureExpiration(),
+ })
+
+ var gotPing, gotPong bool
+ for !gotPing || !gotPong {
+ req, hash, err := te.read(te.l1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch req.(type) {
+ case *v4wire.Ping:
+ te.send(te.l1, &v4wire.Pong{
+ To: te.remoteEndpoint(),
+ ReplyTok: hash,
+ Expiration: futureExpiration(),
+ })
+ gotPing = true
+ case *v4wire.Pong:
+ // TODO: maybe verify pong data here
+ gotPong = true
+ }
+ }
+}
+
+// This test attempts to perform a traffic amplification attack against a
+// 'victim' endpoint using FINDNODE. In this attack scenario, the attacker
+// attempts to complete the endpoint proof non-interactively by sending a PONG
+// with mismatching reply token from the 'victim' endpoint. The attack works if
+// the remote node does not verify the PONG reply token field correctly. The
+// attacker could then perform traffic amplification by sending many FINDNODE
+// requests to the discovery node, which would reply to the 'victim' address.
+func FindnodeAmplificationInvalidPongHash(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ // Send PING to start endpoint verification.
+ te.send(te.l1, &v4wire.Ping{
+ Version: 4,
+ From: te.localEndpoint(te.l1),
+ To: te.remoteEndpoint(),
+ Expiration: futureExpiration(),
+ })
+
+ var gotPing, gotPong bool
+ for !gotPing || !gotPong {
+ req, _, err := te.read(te.l1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ switch req.(type) {
+ case *v4wire.Ping:
+ // Send PONG from this node ID, but with invalid ReplyTok.
+ te.send(te.l1, &v4wire.Pong{
+ To: te.remoteEndpoint(),
+ ReplyTok: make([]byte, macSize),
+ Expiration: futureExpiration(),
+ })
+ gotPing = true
+ case *v4wire.Pong:
+ gotPong = true
+ }
+ }
+
+ // Now send FINDNODE. The remote node should not respond because our
+ // PONG did not reference the PING hash.
+ findnode := v4wire.Findnode{Expiration: futureExpiration()}
+ rand.Read(findnode.Target[:])
+ te.send(te.l1, &findnode)
+
+ // If we receive a NEIGHBORS response, the attack worked and the test fails.
+ reply, _, _ := te.read(te.l1)
+ if reply != nil && reply.Kind() == v4wire.NeighborsPacket {
+ t.Error("Got neighbors")
+ }
+}
+
+// This test attempts to perform a traffic amplification attack using FINDNODE.
+// The attack works if the remote node does not verify the IP address of FINDNODE
+// against the endpoint verification proof done by PING/PONG.
+func FindnodeAmplificationWrongIP(t *utesting.T) {
+ te := newTestEnv(Remote, Listen1, Listen2)
+ defer te.close()
+
+ // Do the endpoint proof from the l1 IP.
+ bond(t, te)
+
+ // Now send FINDNODE from the same node ID, but different IP address.
+ // The remote node should not respond.
+ findnode := v4wire.Findnode{Expiration: futureExpiration()}
+ rand.Read(findnode.Target[:])
+ te.send(te.l2, &findnode)
+
+ // If we receive a NEIGHBORS response, the attack worked and the test fails.
+ reply, _, _ := te.read(te.l2)
+ if reply != nil {
+ t.Error("Got NEIGHORS response for FINDNODE from wrong IP")
+ }
+}
+
+var AllTests = []utesting.Test{
+ {Name: "Ping/Basic", Fn: BasicPing},
+ {Name: "Ping/WrongTo", Fn: PingWrongTo},
+ {Name: "Ping/WrongFrom", Fn: PingWrongFrom},
+ {Name: "Ping/ExtraData", Fn: PingExtraData},
+ {Name: "Ping/ExtraDataWrongFrom", Fn: PingExtraDataWrongFrom},
+ {Name: "Ping/PastExpiration", Fn: PingPastExpiration},
+ {Name: "Ping/WrongPacketType", Fn: WrongPacketType},
+ {Name: "Ping/BondThenPingWithWrongFrom", Fn: BondThenPingWithWrongFrom},
+ {Name: "Findnode/WithoutEndpointProof", Fn: FindnodeWithoutEndpointProof},
+ {Name: "Findnode/BasicFindnode", Fn: BasicFindnode},
+ {Name: "Findnode/UnsolicitedNeighbors", Fn: UnsolicitedNeighbors},
+ {Name: "Findnode/PastExpiration", Fn: FindnodePastExpiration},
+ {Name: "Amplification/InvalidPongHash", Fn: FindnodeAmplificationInvalidPongHash},
+ {Name: "Amplification/WrongIP", Fn: FindnodeAmplificationWrongIP},
+}
diff --git a/cmd/devp2p/internal/v4test/framework.go b/cmd/devp2p/internal/v4test/framework.go
new file mode 100644
index 0000000000..020b8a9bb2
--- /dev/null
+++ b/cmd/devp2p/internal/v4test/framework.go
@@ -0,0 +1,123 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package v4test
+
+import (
+ "crypto/ecdsa"
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/celo-org/celo-blockchain/crypto"
+ "github.com/celo-org/celo-blockchain/p2p/discover/v4wire"
+ "github.com/celo-org/celo-blockchain/p2p/enode"
+)
+
+const waitTime = 300 * time.Millisecond
+
+type testenv struct {
+ l1, l2 net.PacketConn
+ key *ecdsa.PrivateKey
+ remote *enode.Node
+ remoteAddr *net.UDPAddr
+}
+
+func newTestEnv(remote string, listen1, listen2 string) *testenv {
+ l1, err := net.ListenPacket("udp", fmt.Sprintf("%v:0", listen1))
+ if err != nil {
+ panic(err)
+ }
+ l2, err := net.ListenPacket("udp", fmt.Sprintf("%v:0", listen2))
+ if err != nil {
+ panic(err)
+ }
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ panic(err)
+ }
+ node, err := enode.Parse(enode.ValidSchemes, remote)
+ if err != nil {
+ panic(err)
+ }
+ if node.IP() == nil || node.UDP() == 0 {
+ var ip net.IP
+ var tcpPort, udpPort int
+ if ip = node.IP(); ip == nil {
+ ip = net.ParseIP("127.0.0.1")
+ }
+ if tcpPort = node.TCP(); tcpPort == 0 {
+ tcpPort = 30303
+ }
+ if udpPort = node.TCP(); udpPort == 0 {
+ udpPort = 30303
+ }
+ node = enode.NewV4(node.Pubkey(), ip, tcpPort, udpPort)
+ }
+ addr := &net.UDPAddr{IP: node.IP(), Port: node.UDP()}
+ return &testenv{l1, l2, key, node, addr}
+}
+
+func (te *testenv) close() {
+ te.l1.Close()
+ te.l2.Close()
+}
+
+func (te *testenv) send(c net.PacketConn, req v4wire.Packet) []byte {
+ packet, hash, err := v4wire.Encode(te.key, req)
+ if err != nil {
+ panic(fmt.Errorf("can't encode %v packet: %v", req.Name(), err))
+ }
+ if _, err := c.WriteTo(packet, te.remoteAddr); err != nil {
+ panic(fmt.Errorf("can't send %v: %v", req.Name(), err))
+ }
+ return hash
+}
+
+func (te *testenv) read(c net.PacketConn) (v4wire.Packet, []byte, error) {
+ buf := make([]byte, 2048)
+ if err := c.SetReadDeadline(time.Now().Add(waitTime)); err != nil {
+ return nil, nil, err
+ }
+ n, _, err := c.ReadFrom(buf)
+ if err != nil {
+ return nil, nil, err
+ }
+ p, _, hash, err := v4wire.Decode(buf[:n])
+ return p, hash, err
+}
+
+func (te *testenv) localEndpoint(c net.PacketConn) v4wire.Endpoint {
+ addr := c.LocalAddr().(*net.UDPAddr)
+ return v4wire.Endpoint{
+ IP: addr.IP.To4(),
+ UDP: uint16(addr.Port),
+ TCP: 0,
+ }
+}
+
+func (te *testenv) remoteEndpoint() v4wire.Endpoint {
+ return v4wire.NewEndpoint(te.remoteAddr, 0)
+}
+
+func contains(ns []v4wire.Node, key v4wire.Pubkey) bool {
+ for _, n := range ns {
+ if n.ID == key {
+ return true
+ }
+ }
+ return false
+}
diff --git a/cmd/devp2p/keycmd.go b/cmd/devp2p/keycmd.go
new file mode 100644
index 0000000000..edc2c6b4a3
--- /dev/null
+++ b/cmd/devp2p/keycmd.go
@@ -0,0 +1,105 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/celo-org/celo-blockchain/crypto"
+ "github.com/celo-org/celo-blockchain/p2p/enode"
+ "gopkg.in/urfave/cli.v1"
+)
+
+var (
+ keyCommand = cli.Command{
+ Name: "key",
+ Usage: "Operations on node keys",
+ Subcommands: []cli.Command{
+ keyGenerateCommand,
+ keyToNodeCommand,
+ },
+ }
+ keyGenerateCommand = cli.Command{
+ Name: "generate",
+ Usage: "Generates node key files",
+ ArgsUsage: "keyfile",
+ Action: genkey,
+ }
+ keyToNodeCommand = cli.Command{
+ Name: "to-enode",
+ Usage: "Creates an enode URL from a node key file",
+ ArgsUsage: "keyfile",
+ Action: keyToURL,
+ Flags: []cli.Flag{hostFlag, tcpPortFlag, udpPortFlag},
+ }
+)
+
+var (
+ hostFlag = cli.StringFlag{
+ Name: "ip",
+ Usage: "IP address of the node",
+ Value: "127.0.0.1",
+ }
+ tcpPortFlag = cli.IntFlag{
+ Name: "tcp",
+ Usage: "TCP port of the node",
+ Value: 30303,
+ }
+ udpPortFlag = cli.IntFlag{
+ Name: "udp",
+ Usage: "UDP port of the node",
+ Value: 30303,
+ }
+)
+
+func genkey(ctx *cli.Context) error {
+ if ctx.NArg() != 1 {
+ return fmt.Errorf("need key file as argument")
+ }
+ file := ctx.Args().Get(0)
+
+ key, err := crypto.GenerateKey()
+ if err != nil {
+ return fmt.Errorf("could not generate key: %v", err)
+ }
+ return crypto.SaveECDSA(file, key)
+}
+
+func keyToURL(ctx *cli.Context) error {
+ if ctx.NArg() != 1 {
+ return fmt.Errorf("need key file as argument")
+ }
+
+ var (
+ file = ctx.Args().Get(0)
+ host = ctx.String(hostFlag.Name)
+ tcp = ctx.Int(tcpPortFlag.Name)
+ udp = ctx.Int(udpPortFlag.Name)
+ )
+ key, err := crypto.LoadECDSA(file)
+ if err != nil {
+ return err
+ }
+ ip := net.ParseIP(host)
+ if ip == nil {
+ return fmt.Errorf("invalid IP address %q", host)
+ }
+ node := enode.NewV4(&key.PublicKey, ip, tcp, udp)
+ fmt.Println(node.URLv4())
+ return nil
+}
diff --git a/cmd/devp2p/main.go b/cmd/devp2p/main.go
index a603847e01..f56b179297 100644
--- a/cmd/devp2p/main.go
+++ b/cmd/devp2p/main.go
@@ -58,6 +58,7 @@ func init() {
// Add subcommands.
app.Commands = []cli.Command{
enrdumpCommand,
+ keyCommand,
discv4Command,
discv5Command,
dnsCommand,
diff --git a/cmd/ethkey/changepassword.go b/cmd/ethkey/changepassword.go
index 732d90916c..a25ca3fdea 100644
--- a/cmd/ethkey/changepassword.go
+++ b/cmd/ethkey/changepassword.go
@@ -51,7 +51,7 @@ Change the password of a keyfile.`,
}
// Decrypt key with passphrase.
- passphrase := getPassphrase(ctx)
+ passphrase := getPassphrase(ctx, false)
key, err := keystore.DecryptKey(keyjson, passphrase)
if err != nil {
utils.Fatalf("Error decrypting key: %v", err)
@@ -67,7 +67,7 @@ Change the password of a keyfile.`,
}
newPhrase = strings.TrimRight(string(content), "\r\n")
} else {
- newPhrase = promptPassphrase(true)
+ newPhrase = utils.GetPassPhrase("", true)
}
// Encrypt the key with the new passphrase.
diff --git a/cmd/ethkey/generate.go b/cmd/ethkey/generate.go
index 0634b0c0f7..0eeeaaaa47 100644
--- a/cmd/ethkey/generate.go
+++ b/cmd/ethkey/generate.go
@@ -94,7 +94,7 @@ If you want to encrypt an existing private key, it can be specified by setting
}
// Encrypt key with passphrase.
- passphrase := promptPassphrase(true)
+ passphrase := getPassphrase(ctx, true)
scryptN, scryptP := keystore.StandardScryptN, keystore.StandardScryptP
if ctx.Bool("lightkdf") {
scryptN, scryptP = keystore.LightScryptN, keystore.LightScryptP
diff --git a/cmd/ethkey/inspect.go b/cmd/ethkey/inspect.go
index b8c9ea15ed..d31752d9a6 100644
--- a/cmd/ethkey/inspect.go
+++ b/cmd/ethkey/inspect.go
@@ -60,7 +60,7 @@ make sure to use this feature with great caution!`,
}
// Decrypt key with passphrase.
- passphrase := getPassphrase(ctx)
+ passphrase := getPassphrase(ctx, false)
key, err := keystore.DecryptKey(keyjson, passphrase)
if err != nil {
utils.Fatalf("Error decrypting key: %v", err)
diff --git a/cmd/ethkey/message.go b/cmd/ethkey/message.go
index 47b8684550..bdaa8a8ad0 100644
--- a/cmd/ethkey/message.go
+++ b/cmd/ethkey/message.go
@@ -62,7 +62,7 @@ To sign a message contained in a file, use the --msgfile flag.
}
// Decrypt key with passphrase.
- passphrase := getPassphrase(ctx)
+ passphrase := getPassphrase(ctx, false)
key, err := keystore.DecryptKey(keyjson, passphrase)
if err != nil {
utils.Fatalf("Error decrypting key: %v", err)
diff --git a/cmd/ethkey/utils.go b/cmd/ethkey/utils.go
index b31260c7ef..b5903b7f5f 100644
--- a/cmd/ethkey/utils.go
+++ b/cmd/ethkey/utils.go
@@ -23,36 +23,14 @@ import (
"strings"
"github.com/celo-org/celo-blockchain/cmd/utils"
- "github.com/celo-org/celo-blockchain/console/prompt"
"github.com/celo-org/celo-blockchain/crypto"
"gopkg.in/urfave/cli.v1"
)
-// promptPassphrase prompts the user for a passphrase. Set confirmation to true
-// to require the user to confirm the passphrase.
-func promptPassphrase(confirmation bool) string {
- passphrase, err := prompt.Stdin.PromptPassword("Password: ")
- if err != nil {
- utils.Fatalf("Failed to read password: %v", err)
- }
-
- if confirmation {
- confirm, err := prompt.Stdin.PromptPassword("Repeat password: ")
- if err != nil {
- utils.Fatalf("Failed to read password confirmation: %v", err)
- }
- if passphrase != confirm {
- utils.Fatalf("Passwords do not match")
- }
- }
-
- return passphrase
-}
-
// getPassphrase obtains a passphrase given by the user. It first checks the
// --passfile command line flag and ultimately prompts the user for a
// passphrase.
-func getPassphrase(ctx *cli.Context) string {
+func getPassphrase(ctx *cli.Context, confirmation bool) string {
// Look for the --passwordfile flag.
passphraseFile := ctx.String(passphraseFlag.Name)
if passphraseFile != "" {
@@ -65,7 +43,7 @@ func getPassphrase(ctx *cli.Context) string {
}
// Otherwise prompt the user for the passphrase.
- return promptPassphrase(false)
+ return utils.GetPassPhrase("", confirmation)
}
// signHash is a helper function that calculates a hash for the given message
diff --git a/cmd/evm/README.md b/cmd/evm/README.md
new file mode 100644
index 0000000000..418417475d
--- /dev/null
+++ b/cmd/evm/README.md
@@ -0,0 +1,268 @@
+## EVM state transition tool
+
+The `evm t8n` tool is a stateless state transition utility. It is a utility
+which can
+
+1. Take a prestate, including
+ - Accounts,
+ - Block context information,
+ - Previous blockshashes (*optional)
+2. Apply a set of transactions,
+3. Apply a mining-reward (*optional),
+4. And generate a post-state, including
+ - State root, transaction root, receipt root,
+ - Information about rejected transactions,
+ - Optionally: a full or partial post-state dump
+
+## Specification
+
+The idea is to specify the behaviour of this binary very _strict_, so that other
+node implementors can build replicas based on their own state-machines, and the
+state generators can swap between a `geth`-based implementation and a `parityvm`-based
+implementation.
+
+### Command line params
+
+Command line params that has to be supported are
+```
+
+ --trace Output full trace logs to files .jsonl
+ --trace.nomemory Disable full memory dump in traces
+ --trace.nostack Disable stack output in traces
+ --output.alloc alloc Determines where to put the alloc of the post-state.
+ `stdout` - into the stdout output
+ `stderr` - into the stderr output
+ --output.result result Determines where to put the result (stateroot, txroot etc) of the post-state.
+ `stdout` - into the stdout output
+ `stderr` - into the stderr output
+ --state.fork value Name of ruleset to use.
+ --state.chainid value ChainID to use (default: 1)
+ --state.reward value Mining reward. Set to -1 to disable (default: 0)
+
+```
+
+### Error codes and output
+
+All logging should happen against the `stderr`.
+There are a few (not many) errors that can occur, those are defined below.
+
+#### EVM-based errors (`2` to `9`)
+
+- Other EVM error. Exit code `2`
+- Failed configuration: when a non-supported or invalid fork was specified. Exit code `3`.
+- Block history is not supplied, but needed for a `BLOCKHASH` operation. If `BLOCKHASH`
+ is invoked targeting a block which history has not been provided for, the program will
+ exit with code `4`.
+
+#### IO errors (`10`-`20`)
+
+- Invalid input json: the supplied data could not be marshalled.
+ The program will exit with code `10`
+- IO problems: failure to load or save files, the program will exit with code `11`
+
+## Examples
+### Basic usage
+
+Invoking it with the provided example files
+```
+./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json
+```
+Two resulting files:
+
+`alloc.json`:
+```json
+{
+ "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": {
+ "balance": "0xfeed1a9d",
+ "nonce": "0x1"
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0x5ffd4878be161d74",
+ "nonce": "0xac"
+ },
+ "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0xa410"
+ }
+}
+```
+`result.json`:
+```json
+{
+ "stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13",
+ "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d",
+ "receiptRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [
+ {
+ "root": "0x",
+ "status": "0x1",
+ "cumulativeGasUsed": "0x5208",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logs": null,
+ "transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
+ "contractAddress": "0x0000000000000000000000000000000000000000",
+ "gasUsed": "0x5208",
+ "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "transactionIndex": "0x0"
+ }
+ ],
+ "rejected": [
+ 1
+ ]
+}
+```
+
+We can make them spit out the data to e.g. `stdout` like this:
+```
+./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.result=stdout --output.alloc=stdout
+```
+Output:
+```json
+{
+ "alloc": {
+ "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": {
+ "balance": "0xfeed1a9d",
+ "nonce": "0x1"
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0x5ffd4878be161d74",
+ "nonce": "0xac"
+ },
+ "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0xa410"
+ }
+ },
+ "result": {
+ "stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13",
+ "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d",
+ "receiptRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [
+ {
+ "root": "0x",
+ "status": "0x1",
+ "cumulativeGasUsed": "0x5208",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logs": null,
+ "transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
+ "contractAddress": "0x0000000000000000000000000000000000000000",
+ "gasUsed": "0x5208",
+ "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "transactionIndex": "0x0"
+ }
+ ],
+ "rejected": [
+ 1
+ ]
+ }
+}
+```
+
+## About Ommers
+
+Mining rewards and ommer rewards might need to be added. This is how those are applied:
+
+- `block_reward` is the block mining reward for the miner (`0xaa`), of a block at height `N`.
+- For each ommer (mined by `0xbb`), with blocknumber `N-delta`
+ - (where `delta` is the difference between the current block and the ommer)
+ - The account `0xbb` (ommer miner) is awarded `(8-delta)/ 8 * block_reward`
+ - The account `0xaa` (block miner) is awarded `block_reward / 32`
+
+To make `state_t8n` apply these, the following inputs are required:
+
+- `state.reward`
+ - For ethash, it is `5000000000000000000` `wei`,
+ - If this is not defined, mining rewards are not applied,
+ - A value of `0` is valid, and causes accounts to be 'touched'.
+- For each ommer, the tool needs to be given an `address` and a `delta`. This
+ is done via the `env`.
+
+Note: the tool does not verify that e.g. the normal uncle rules apply,
+and allows e.g two uncles at the same height, or the uncle-distance. This means that
+the tool allows for negative uncle reward (distance > 8)
+
+Example:
+`./testdata/5/env.json`:
+```json
+{
+ "currentCoinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "currentDifficulty": "0x20000",
+ "currentGasLimit": "0x750a163df65e8a",
+ "currentNumber": "1",
+ "currentTimestamp": "1000",
+ "ommers": [
+ {"delta": 1, "address": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" },
+ {"delta": 2, "address": "0xcccccccccccccccccccccccccccccccccccccccc" }
+ ]
+}
+```
+When applying this, using a reward of `0x08`
+Output:
+```json
+{
+ "alloc": {
+ "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": {
+ "balance": "0x88"
+ },
+ "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": {
+ "balance": "0x70"
+ },
+ "0xcccccccccccccccccccccccccccccccccccccccc": {
+ "balance": "0x60"
+ }
+ }
+}
+```
+### Future EIPS
+
+It is also possible to experiment with future eips that are not yet defined in a hard fork.
+Example, putting EIP-1344 into Frontier:
+```
+./evm t8n --state.fork=Frontier+1344 --input.pre=./testdata/1/pre.json --input.txs=./testdata/1/txs.json --input.env=/testdata/1/env.json
+```
+
+### Block history
+
+The `BLOCKHASH` opcode requires blockhashes to be provided by the caller, inside the `env`.
+If a required blockhash is not provided, the exit code should be `4`:
+Example where blockhashes are provided:
+```
+./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace
+```
+```
+cat trace-0.jsonl | grep BLOCKHASH -C2
+```
+```
+{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"depth":1,"refund":0,"opName":"PUSH1","error":""}
+{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
+{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"depth":1,"refund":0,"opName":"STOP","error":""}
+{"output":"","gasUsed":"0x17","time":155861}
+```
+
+In this example, the caller has not provided the required blockhash:
+```
+./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace
+```
+```
+ERROR(4): getHash(3) invoked, blockhash for that block not provided
+```
+Error code: 4
+### Chaining
+
+Another thing that can be done, is to chain invocations:
+```
+./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json
+INFO [06-29|11:52:04.934] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
+INFO [06-29|11:52:04.936] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
+INFO [06-29|11:52:04.936] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
+
+```
+What happened here, is that we first applied two identical transactions, so the second one was rejected.
+Then, taking the poststate alloc as the input for the next state, we tried again to include
+the same two transactions: this time, both failed due to too low nonce.
+
+In order to meaningfully chain invocations, one would need to provide meaningful new `env`, otherwise the
+actual blocknumber (exposed to the EVM) would not increase.
+
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
new file mode 100644
index 0000000000..6ca65b11d6
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -0,0 +1,253 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package t8ntool
+
+import (
+ "fmt"
+ "math/big"
+ "os"
+
+ "github.com/celo-org/celo-blockchain/common"
+ "github.com/celo-org/celo-blockchain/common/math"
+ "github.com/celo-org/celo-blockchain/consensus/misc"
+ "github.com/celo-org/celo-blockchain/core"
+ "github.com/celo-org/celo-blockchain/core/rawdb"
+ "github.com/celo-org/celo-blockchain/core/state"
+ "github.com/celo-org/celo-blockchain/core/types"
+ "github.com/celo-org/celo-blockchain/core/vm"
+ "github.com/celo-org/celo-blockchain/crypto"
+ "github.com/celo-org/celo-blockchain/ethdb"
+ "github.com/celo-org/celo-blockchain/log"
+ "github.com/celo-org/celo-blockchain/params"
+ "github.com/celo-org/celo-blockchain/rlp"
+ "golang.org/x/crypto/sha3"
+)
+
+type Prestate struct {
+ Env stEnv `json:"env"`
+ Pre core.GenesisAlloc `json:"pre"`
+}
+
+// ExecutionResult contains the execution status after running a state test, any
+// error that might have occurred and a dump of the final state if requested.
+type ExecutionResult struct {
+ StateRoot common.Hash `json:"stateRoot"`
+ TxRoot common.Hash `json:"txRoot"`
+ ReceiptRoot common.Hash `json:"receiptRoot"`
+ LogsHash common.Hash `json:"logsHash"`
+ Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
+ Receipts types.Receipts `json:"receipts"`
+ Rejected []int `json:"rejected,omitempty"`
+}
+
+type ommer struct {
+ Delta uint64 `json:"delta"`
+ Address common.Address `json:"address"`
+}
+
+//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
+type stEnv struct {
+ Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
+ GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
+ Number uint64 `json:"currentNumber" gencodec:"required"`
+ Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
+ BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
+ Ommers []ommer `json:"ommers,omitempty"`
+}
+
+type stEnvMarshaling struct {
+ Coinbase common.UnprefixedAddress
+ Difficulty *math.HexOrDecimal256
+ GasLimit math.HexOrDecimal64
+ Number math.HexOrDecimal64
+ Timestamp math.HexOrDecimal64
+}
+
+// Apply applies a set of transactions to a pre-state
+func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
+ txs types.Transactions, miningReward int64,
+ getTracerFn func(txIndex int) (tracer vm.Tracer, err error)) (*state.StateDB, *ExecutionResult, error) {
+
+ // Capture errors for BLOCKHASH operation, if we haven't been supplied the
+ // required blockhashes
+ var hashError error
+ getHash := func(num uint64) common.Hash {
+ if pre.Env.BlockHashes == nil {
+ hashError = fmt.Errorf("getHash(%d) invoked, no blockhashes provided", num)
+ return common.Hash{}
+ }
+ h, ok := pre.Env.BlockHashes[math.HexOrDecimal64(num)]
+ if !ok {
+ hashError = fmt.Errorf("getHash(%d) invoked, blockhash for that block not provided", num)
+ }
+ return h
+ }
+ var (
+ statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre)
+ signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number))
+ gaspool = new(core.GasPool)
+ blockHash = common.Hash{0x13, 0x37}
+ rejectedTxs []int
+ includedTxs types.Transactions
+ gasUsed = uint64(0)
+ receipts = make(types.Receipts, 0)
+ txIndex = 0
+ )
+ gaspool.AddGas(pre.Env.GasLimit)
+ vmContext := vm.Context{
+ // CanTransfer: core.CanTransfer,
+ // Transfer: core.Transfer,
+ Coinbase: pre.Env.Coinbase,
+ BlockNumber: new(big.Int).SetUint64(pre.Env.Number),
+ Time: new(big.Int).SetUint64(pre.Env.Timestamp),
+ GetHash: getHash,
+ // GasPrice and Origin needs to be set per transaction
+ }
+ // If DAO is supported/enabled, we need to handle it here. In geth 'proper', it's
+ // done in StateProcessor.Process(block, ...), right before transactions are applied.
+ if chainConfig.DAOForkSupport &&
+ chainConfig.DAOForkBlock != nil &&
+ chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
+ misc.ApplyDAOHardFork(statedb)
+ }
+
+ for i, tx := range txs {
+ msg, err := tx.AsMessage(signer)
+ if err != nil {
+ log.Info("rejected tx", "index", i, "hash", tx.Hash(), "error", err)
+ rejectedTxs = append(rejectedTxs, i)
+ continue
+ }
+ tracer, err := getTracerFn(txIndex)
+ if err != nil {
+ return nil, nil, err
+ }
+ vmConfig.Tracer = tracer
+ vmConfig.Debug = (tracer != nil)
+ statedb.Prepare(tx.Hash(), blockHash, txIndex)
+ vmContext.GasPrice = msg.GasPrice()
+ vmContext.Origin = msg.From()
+
+ evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig)
+ snapshot := statedb.Snapshot()
+ // (ret []byte, usedGas uint64, failed bool, err error)
+ msgResult, err := core.ApplyMessage(evm, msg, gaspool)
+ if err != nil {
+ statedb.RevertToSnapshot(snapshot)
+ log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From(), "error", err)
+ rejectedTxs = append(rejectedTxs, i)
+ continue
+ }
+ includedTxs = append(includedTxs, tx)
+ if hashError != nil {
+ return nil, nil, NewError(ErrorMissingBlockhash, hashError)
+ }
+ gasUsed += msgResult.UsedGas
+ // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
+ {
+ var root []byte
+ if chainConfig.IsByzantium(vmContext.BlockNumber) {
+ statedb.Finalise(true)
+ } else {
+ root = statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)).Bytes()
+ }
+
+ receipt := types.NewReceipt(root, msgResult.Failed(), gasUsed)
+ receipt.TxHash = tx.Hash()
+ receipt.GasUsed = msgResult.UsedGas
+ // if the transaction created a contract, store the creation address in the receipt.
+ if msg.To() == nil {
+ receipt.ContractAddress = crypto.CreateAddress(evm.Context.Origin, tx.Nonce())
+ }
+ // Set the receipt logs and create a bloom for filtering
+ receipt.Logs = statedb.GetLogs(tx.Hash())
+ receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
+ // These three are non-consensus fields
+ //receipt.BlockHash
+ //receipt.BlockNumber =
+ receipt.TransactionIndex = uint(txIndex)
+ receipts = append(receipts, receipt)
+ }
+ txIndex++
+ }
+ statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber))
+ // Add mining reward?
+ if miningReward > 0 {
+ // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases
+ // where
+ // - the coinbase suicided, or
+ // - there are only 'bad' transactions, which aren't executed. In those cases,
+ // the coinbase gets no txfee, so isn't created, and thus needs to be touched
+ var (
+ blockReward = big.NewInt(miningReward)
+ minerReward = new(big.Int).Set(blockReward)
+ perOmmer = new(big.Int).Div(blockReward, big.NewInt(32))
+ )
+ for _, ommer := range pre.Env.Ommers {
+ // Add 1/32th for each ommer included
+ minerReward.Add(minerReward, perOmmer)
+ // Add (8-delta)/8
+ reward := big.NewInt(8)
+ reward.Sub(reward, big.NewInt(0).SetUint64(ommer.Delta))
+ reward.Mul(reward, blockReward)
+ reward.Div(reward, big.NewInt(8))
+ statedb.AddBalance(ommer.Address, reward)
+ }
+ statedb.AddBalance(pre.Env.Coinbase, minerReward)
+ }
+ // Commit block
+ root, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber))
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Could not commit state: %v", err)
+ return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err))
+ }
+ execRs := &ExecutionResult{
+ StateRoot: root,
+ TxRoot: types.DeriveSha(includedTxs),
+ ReceiptRoot: types.DeriveSha(receipts),
+ Bloom: types.CreateBloom(receipts),
+ LogsHash: rlpHash(statedb.Logs()),
+ Receipts: receipts,
+ Rejected: rejectedTxs,
+ }
+ return statedb, execRs, nil
+}
+
+func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
+ sdb := state.NewDatabase(db)
+ statedb, _ := state.New(common.Hash{}, sdb, nil)
+ for addr, a := range accounts {
+ statedb.SetCode(addr, a.Code)
+ statedb.SetNonce(addr, a.Nonce)
+ statedb.SetBalance(addr, a.Balance)
+ for k, v := range a.Storage {
+ statedb.SetState(addr, k, v)
+ }
+ }
+ // Commit and re-open to start with a clean state.
+ root, _ := statedb.Commit(false)
+ statedb, _ = state.New(root, sdb, nil)
+ return statedb
+}
+
+func rlpHash(x interface{}) (h common.Hash) {
+ hw := sha3.NewLegacyKeccak256()
+ rlp.Encode(hw, x)
+ hw.Sum(h[:0])
+ return h
+}
diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go
new file mode 100644
index 0000000000..008780593f
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/flags.go
@@ -0,0 +1,99 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package t8ntool
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/celo-org/celo-blockchain/core/vm"
+ "github.com/celo-org/celo-blockchain/tests"
+ "gopkg.in/urfave/cli.v1"
+)
+
+var (
+ TraceFlag = cli.BoolFlag{
+ Name: "trace",
+ Usage: "Output full trace logs to files .jsonl",
+ }
+ TraceDisableMemoryFlag = cli.BoolFlag{
+ Name: "trace.nomemory",
+ Usage: "Disable full memory dump in traces",
+ }
+ TraceDisableStackFlag = cli.BoolFlag{
+ Name: "trace.nostack",
+ Usage: "Disable stack output in traces",
+ }
+ OutputAllocFlag = cli.StringFlag{
+ Name: "output.alloc",
+ Usage: "Determines where to put the `alloc` of the post-state.\n" +
+ "\t`stdout` - into the stdout output\n" +
+ "\t`stderr` - into the stderr output\n" +
+ "\t - into the file ",
+ Value: "alloc.json",
+ }
+ OutputResultFlag = cli.StringFlag{
+ Name: "output.result",
+ Usage: "Determines where to put the `result` (stateroot, txroot etc) of the post-state.\n" +
+ "\t`stdout` - into the stdout output\n" +
+ "\t`stderr` - into the stderr output\n" +
+ "\t - into the file ",
+ Value: "result.json",
+ }
+ InputAllocFlag = cli.StringFlag{
+ Name: "input.alloc",
+ Usage: "`stdin` or file name of where to find the prestate alloc to use.",
+ Value: "alloc.json",
+ }
+ InputEnvFlag = cli.StringFlag{
+ Name: "input.env",
+ Usage: "`stdin` or file name of where to find the prestate env to use.",
+ Value: "env.json",
+ }
+ InputTxsFlag = cli.StringFlag{
+ Name: "input.txs",
+ Usage: "`stdin` or file name of where to find the transactions to apply.",
+ Value: "txs.json",
+ }
+ RewardFlag = cli.Int64Flag{
+ Name: "state.reward",
+ Usage: "Mining reward. Set to -1 to disable",
+ Value: 0,
+ }
+ ChainIDFlag = cli.Int64Flag{
+ Name: "state.chainid",
+ Usage: "ChainID to use",
+ Value: 1,
+ }
+ ForknameFlag = cli.StringFlag{
+ Name: "state.fork",
+ Usage: fmt.Sprintf("Name of ruleset to use."+
+ "\n\tAvailable forknames:"+
+ "\n\t %v"+
+ "\n\tAvailable extra eips:"+
+ "\n\t %v"+
+ "\n\tSyntax (+ExtraEip)",
+ strings.Join(tests.AvailableForks(), "\n\t "),
+ strings.Join(vm.ActivateableEips(), ", ")),
+ Value: "Istanbul",
+ }
+ VerbosityFlag = cli.IntFlag{
+ Name: "verbosity",
+ Usage: "sets the verbosity level",
+ Value: 3,
+ }
+)
diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go
new file mode 100644
index 0000000000..c3dcd7e6a6
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/gen_stenv.go
@@ -0,0 +1,80 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package t8ntool
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+
+ "github.com/celo-org/celo-blockchain/common"
+ "github.com/celo-org/celo-blockchain/common/math"
+)
+
+var _ = (*stEnvMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (s stEnv) MarshalJSON() ([]byte, error) {
+ type stEnv struct {
+ Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
+ GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
+ Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
+ Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
+ Ommers []ommer `json:"ommers,omitempty"`
+ }
+ var enc stEnv
+ enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
+ enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty)
+ enc.GasLimit = math.HexOrDecimal64(s.GasLimit)
+ enc.Number = math.HexOrDecimal64(s.Number)
+ enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
+ enc.BlockHashes = s.BlockHashes
+ enc.Ommers = s.Ommers
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (s *stEnv) UnmarshalJSON(input []byte) error {
+ type stEnv struct {
+ Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
+ GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
+ Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
+ Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
+ Ommers []ommer `json:"ommers,omitempty"`
+ }
+ var dec stEnv
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Coinbase == nil {
+ return errors.New("missing required field 'currentCoinbase' for stEnv")
+ }
+ s.Coinbase = common.Address(*dec.Coinbase)
+ if dec.Difficulty == nil {
+ return errors.New("missing required field 'currentDifficulty' for stEnv")
+ }
+ s.Difficulty = (*big.Int)(dec.Difficulty)
+ if dec.GasLimit == nil {
+ return errors.New("missing required field 'currentGasLimit' for stEnv")
+ }
+ s.GasLimit = uint64(*dec.GasLimit)
+ if dec.Number == nil {
+ return errors.New("missing required field 'currentNumber' for stEnv")
+ }
+ s.Number = uint64(*dec.Number)
+ if dec.Timestamp == nil {
+ return errors.New("missing required field 'currentTimestamp' for stEnv")
+ }
+ s.Timestamp = uint64(*dec.Timestamp)
+ if dec.BlockHashes != nil {
+ s.BlockHashes = dec.BlockHashes
+ }
+ if dec.Ommers != nil {
+ s.Ommers = dec.Ommers
+ }
+ return nil
+}
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
new file mode 100644
index 0000000000..2b2f3e2057
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -0,0 +1,276 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see .
+
+package t8ntool
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "math/big"
+ "os"
+
+ "github.com/celo-org/celo-blockchain/common"
+ "github.com/celo-org/celo-blockchain/core"
+ "github.com/celo-org/celo-blockchain/core/state"
+ "github.com/celo-org/celo-blockchain/core/types"
+ "github.com/celo-org/celo-blockchain/core/vm"
+ "github.com/celo-org/celo-blockchain/log"
+ "github.com/celo-org/celo-blockchain/params"
+ "github.com/celo-org/celo-blockchain/tests"
+ "gopkg.in/urfave/cli.v1"
+)
+
+const (
+ ErrorEVM = 2
+ ErrorVMConfig = 3
+ ErrorMissingBlockhash = 4
+
+ ErrorJson = 10
+ ErrorIO = 11
+
+ stdinSelector = "stdin"
+)
+
+type NumberedError struct {
+ errorCode int
+ err error
+}
+
+func NewError(errorCode int, err error) *NumberedError {
+ return &NumberedError{errorCode, err}
+}
+
+func (n *NumberedError) Error() string {
+ return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
+}
+
+func (n *NumberedError) Code() int {
+ return n.errorCode
+}
+
+type input struct {
+ Alloc core.GenesisAlloc `json:"alloc,omitempty"`
+ Env *stEnv `json:"env,omitempty"`
+ Txs types.Transactions `json:"txs,omitempty"`
+}
+
+func Main(ctx *cli.Context) error {
+ // Configure the go-ethereum logger
+ glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
+ glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
+ log.Root().SetHandler(glogger)
+
+ var (
+ err error
+ tracer vm.Tracer
+ )
+ var getTracer func(txIndex int) (vm.Tracer, error)
+
+ if ctx.Bool(TraceFlag.Name) {
+ // Configure the EVM logger
+ logConfig := &vm.LogConfig{
+ DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
+ DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
+ Debug: true,
+ }
+ var prevFile *os.File
+ // This one closes the last file
+ defer func() {
+ if prevFile != nil {
+ prevFile.Close()
+ }
+ }()
+ getTracer = func(txIndex int) (vm.Tracer, error) {
+ if prevFile != nil {
+ prevFile.Close()
+ }
+ traceFile, err := os.Create(fmt.Sprintf("trace-%d.jsonl", txIndex))
+ if err != nil {
+ return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
+ }
+ prevFile = traceFile
+ return vm.NewJSONLogger(logConfig, traceFile), nil
+ }
+ } else {
+ getTracer = func(txIndex int) (tracer vm.Tracer, err error) {
+ return nil, nil
+ }
+ }
+ // We need to load three things: alloc, env and transactions. May be either in
+ // stdin input or in files.
+ // Check if anything needs to be read from stdin
+ var (
+ prestate Prestate
+ txs types.Transactions // txs to apply
+ allocStr = ctx.String(InputAllocFlag.Name)
+
+ envStr = ctx.String(InputEnvFlag.Name)
+ txStr = ctx.String(InputTxsFlag.Name)
+ inputData = &input{}
+ )
+
+ if allocStr == stdinSelector || envStr == stdinSelector || txStr == stdinSelector {
+ decoder := json.NewDecoder(os.Stdin)
+ decoder.Decode(inputData)
+ }
+ if allocStr != stdinSelector {
+ inFile, err := os.Open(allocStr)
+ if err != nil {
+ return NewError(ErrorIO, fmt.Errorf("failed reading alloc file: %v", err))
+ }
+ defer inFile.Close()
+ decoder := json.NewDecoder(inFile)
+ if err := decoder.Decode(&inputData.Alloc); err != nil {
+ return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling alloc-file: %v", err))
+ }
+ }
+
+ if envStr != stdinSelector {
+ inFile, err := os.Open(envStr)
+ if err != nil {
+ return NewError(ErrorIO, fmt.Errorf("failed reading env file: %v", err))
+ }
+ defer inFile.Close()
+ decoder := json.NewDecoder(inFile)
+ var env stEnv
+ if err := decoder.Decode(&env); err != nil {
+ return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling env-file: %v", err))
+ }
+ inputData.Env = &env
+ }
+
+ if txStr != stdinSelector {
+ inFile, err := os.Open(txStr)
+ if err != nil {
+ return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
+ }
+ defer inFile.Close()
+ decoder := json.NewDecoder(inFile)
+ var txs types.Transactions
+ if err := decoder.Decode(&txs); err != nil {
+ return NewError(ErrorJson, fmt.Errorf("Failed unmarshaling txs-file: %v", err))
+ }
+ inputData.Txs = txs
+ }
+
+ prestate.Pre = inputData.Alloc
+ prestate.Env = *inputData.Env
+ txs = inputData.Txs
+
+ // Iterate over all the tests, run them and aggregate the results
+ vmConfig := vm.Config{
+ Tracer: tracer,
+ Debug: (tracer != nil),
+ }
+ // Construct the chainconfig
+ var chainConfig *params.ChainConfig
+ if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
+ return NewError(ErrorVMConfig, fmt.Errorf("Failed constructing chain configuration: %v", err))
+ } else {
+ chainConfig = cConf
+ vmConfig.ExtraEips = extraEips
+ }
+ // Set the chain id
+ chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
+
+ // Run the test and aggregate the result
+ state, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
+ if err != nil {
+ return err
+ }
+ // Dump the excution result
+ //postAlloc := state.DumpGenesisFormat(false, false, false)
+ collector := make(Alloc)
+ state.DumpToCollector(collector, false, false, false, nil, -1)
+ return dispatchOutput(ctx, result, collector)
+
+}
+
+type Alloc map[common.Address]core.GenesisAccount
+
+func (g Alloc) OnRoot(common.Hash) {}
+
+func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
+ balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
+ var storage map[common.Hash]common.Hash
+ if dumpAccount.Storage != nil {
+ storage = make(map[common.Hash]common.Hash)
+ for k, v := range dumpAccount.Storage {
+ storage[k] = common.HexToHash(v)
+ }
+ }
+ genesisAccount := core.GenesisAccount{
+ Code: common.FromHex(dumpAccount.Code),
+ Storage: storage,
+ Balance: balance,
+ Nonce: dumpAccount.Nonce,
+ }
+ g[addr] = genesisAccount
+}
+
+// saveFile marshalls the object to the given file
+func saveFile(filename string, data interface{}) error {
+ b, err := json.MarshalIndent(data, "", " ")
+ if err != nil {
+ return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
+ }
+ if err = ioutil.WriteFile(filename, b, 0644); err != nil {
+ return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
+ }
+ return nil
+}
+
+// dispatchOutput writes the output data to either stderr or stdout, or to the specified
+// files
+func dispatchOutput(ctx *cli.Context, result *ExecutionResult, alloc Alloc) error {
+ stdOutObject := make(map[string]interface{})
+ stdErrObject := make(map[string]interface{})
+ dispatch := func(fName, name string, obj interface{}) error {
+ switch fName {
+ case "stdout":
+ stdOutObject[name] = obj
+ case "stderr":
+ stdErrObject[name] = obj
+ default: // save to file
+ if err := saveFile(fName, obj); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ if err := dispatch(ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
+ return err
+ }
+ if err := dispatch(ctx.String(OutputResultFlag.Name), "result", result); err != nil {
+ return err
+ }
+ if len(stdOutObject) > 0 {
+ b, err := json.MarshalIndent(stdOutObject, "", " ")
+ if err != nil {
+ return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
+ }
+ os.Stdout.Write(b)
+ }
+ if len(stdErrObject) > 0 {
+ b, err := json.MarshalIndent(stdErrObject, "", " ")
+ if err != nil {
+ return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
+ }
+ os.Stderr.Write(b)
+ }
+ return nil
+}
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index 36d2560800..942e07c7bd 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -22,6 +22,7 @@ import (
"math/big"
"os"
+ "github.com/celo-org/celo-blockchain/cmd/evm/internal/t8ntool"
"github.com/celo-org/celo-blockchain/cmd/utils"
"gopkg.in/urfave/cli.v1"
)
@@ -126,6 +127,27 @@ var (
}
)
+var stateTransitionCommand = cli.Command{
+ Name: "transition",
+ Aliases: []string{"t8n"},
+ Usage: "executes a full state transition",
+ Action: t8ntool.Main,
+ Flags: []cli.Flag{
+ t8ntool.TraceFlag,
+ t8ntool.TraceDisableMemoryFlag,
+ t8ntool.TraceDisableStackFlag,
+ t8ntool.OutputAllocFlag,
+ t8ntool.OutputResultFlag,
+ t8ntool.InputAllocFlag,
+ t8ntool.InputEnvFlag,
+ t8ntool.InputTxsFlag,
+ t8ntool.ForknameFlag,
+ t8ntool.ChainIDFlag,
+ t8ntool.RewardFlag,
+ t8ntool.VerbosityFlag,
+ },
+}
+
func init() {
app.Flags = []cli.Flag{
BenchFlag,
@@ -156,13 +178,18 @@ func init() {
disasmCommand,
runCommand,
stateTestCommand,
+ stateTransitionCommand,
}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
}
func main() {
if err := app.Run(os.Args); err != nil {
+ code := 1
+ if ec, ok := err.(*t8ntool.NumberedError); ok {
+ code = ec.Code()
+ }
fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
+ os.Exit(code)
}
}
diff --git a/cmd/evm/poststate.json b/cmd/evm/poststate.json
new file mode 100644
index 0000000000..9ee17f18d1
--- /dev/null
+++ b/cmd/evm/poststate.json
@@ -0,0 +1,23 @@
+{
+ "root": "f4157bb27bcb1d1a63001434a249a80948f2e9fe1f53d551244c1dae826b5b23",
+ "accounts": {
+ "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": {
+ "balance": "4276951709",
+ "nonce": 1,
+ "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "6916764286133345652",
+ "nonce": 172,
+ "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
+ },
+ "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "42500",
+ "nonce": 0,
+ "root": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/1/alloc.json b/cmd/evm/testdata/1/alloc.json
new file mode 100644
index 0000000000..cef1a25ff0
--- /dev/null
+++ b/cmd/evm/testdata/1/alloc.json
@@ -0,0 +1,12 @@
+{
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0x5ffd4878be161d74",
+ "code": "0x",
+ "nonce": "0xac",
+ "storage": {}
+ },
+ "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{
+ "balance": "0xfeedbead",
+ "nonce" : "0x00"
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/1/env.json b/cmd/evm/testdata/1/env.json
new file mode 100644
index 0000000000..dd60abd205
--- /dev/null
+++ b/cmd/evm/testdata/1/env.json
@@ -0,0 +1,7 @@
+{
+ "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "currentDifficulty": "0x20000",
+ "currentGasLimit": "0x750a163df65e8a",
+ "currentNumber": "1",
+ "currentTimestamp": "1000"
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/1/txs.json b/cmd/evm/testdata/1/txs.json
new file mode 100644
index 0000000000..50b31ff31b
--- /dev/null
+++ b/cmd/evm/testdata/1/txs.json
@@ -0,0 +1,26 @@
+[
+ {
+ "gas": "0x5208",
+ "gasPrice": "0x2",
+ "hash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
+ "input": "0x",
+ "nonce": "0x0",
+ "r": "0x9500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdb",
+ "s": "0x7235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600",
+ "to": "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192",
+ "v": "0x1b",
+ "value": "0x1"
+ },
+ {
+ "gas": "0x5208",
+ "gasPrice": "0x2",
+ "hash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
+ "input": "0x",
+ "nonce": "0x0",
+ "r": "0x9500e8ba27d3c33ca7764e107410f44cbd8c19794bde214d694683a7aa998cdb",
+ "s": "0x7235ae07e4bd6e0206d102b1f8979d6adab280466b6a82d2208ee08951f1f600",
+ "to": "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192",
+ "v": "0x1b",
+ "value": "0x1"
+ }
+]
diff --git a/cmd/evm/testdata/2/alloc.json b/cmd/evm/testdata/2/alloc.json
new file mode 100644
index 0000000000..a9720afc93
--- /dev/null
+++ b/cmd/evm/testdata/2/alloc.json
@@ -0,0 +1,16 @@
+{
+ "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x6001600053600160006001f0ff00",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/2/env.json b/cmd/evm/testdata/2/env.json
new file mode 100644
index 0000000000..ebadd3f06a
--- /dev/null
+++ b/cmd/evm/testdata/2/env.json
@@ -0,0 +1,7 @@
+{
+ "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "currentDifficulty" : "0x020000",
+ "currentGasLimit" : "0x3b9aca00",
+ "currentNumber" : "0x01",
+ "currentTimestamp" : "0x03e8"
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/2/readme.md b/cmd/evm/testdata/2/readme.md
new file mode 100644
index 0000000000..c116f0e792
--- /dev/null
+++ b/cmd/evm/testdata/2/readme.md
@@ -0,0 +1 @@
+These files examplify a selfdestruct to the `0`-address.
\ No newline at end of file
diff --git a/cmd/evm/testdata/2/txs.json b/cmd/evm/testdata/2/txs.json
new file mode 100644
index 0000000000..3044458588
--- /dev/null
+++ b/cmd/evm/testdata/2/txs.json
@@ -0,0 +1,14 @@
+[
+ {
+ "input" : "0x",
+ "gas" : "0x5f5e100",
+ "gasPrice" : "0x1",
+ "nonce" : "0x0",
+ "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "value" : "0x186a0",
+ "v" : "0x1b",
+ "r" : "0x88544c93a564b4c28d2ffac2074a0c55fdd4658fe0d215596ed2e32e3ef7f56b",
+ "s" : "0x7fb4075d54190f825d7c47bb820284757b34fd6293904a93cddb1d3aa961ac28",
+ "hash" : "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81"
+ }
+]
\ No newline at end of file
diff --git a/cmd/evm/testdata/3/alloc.json b/cmd/evm/testdata/3/alloc.json
new file mode 100644
index 0000000000..dca318ee54
--- /dev/null
+++ b/cmd/evm/testdata/3/alloc.json
@@ -0,0 +1,16 @@
+{
+ "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x600140",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/3/env.json b/cmd/evm/testdata/3/env.json
new file mode 100644
index 0000000000..e283eff461
--- /dev/null
+++ b/cmd/evm/testdata/3/env.json
@@ -0,0 +1,8 @@
+{
+ "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "currentDifficulty" : "0x020000",
+ "currentGasLimit" : "0x3b9aca00",
+ "currentNumber" : "0x05",
+ "currentTimestamp" : "0x03e8",
+ "blockHashes" : { "1" : "0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"}
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/3/readme.md b/cmd/evm/testdata/3/readme.md
new file mode 100644
index 0000000000..499f03d7aa
--- /dev/null
+++ b/cmd/evm/testdata/3/readme.md
@@ -0,0 +1,2 @@
+These files examplify a transition where a transaction (excuted on block 5) requests
+the blockhash for block `1`.
diff --git a/cmd/evm/testdata/3/txs.json b/cmd/evm/testdata/3/txs.json
new file mode 100644
index 0000000000..3044458588
--- /dev/null
+++ b/cmd/evm/testdata/3/txs.json
@@ -0,0 +1,14 @@
+[
+ {
+ "input" : "0x",
+ "gas" : "0x5f5e100",
+ "gasPrice" : "0x1",
+ "nonce" : "0x0",
+ "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "value" : "0x186a0",
+ "v" : "0x1b",
+ "r" : "0x88544c93a564b4c28d2ffac2074a0c55fdd4658fe0d215596ed2e32e3ef7f56b",
+ "s" : "0x7fb4075d54190f825d7c47bb820284757b34fd6293904a93cddb1d3aa961ac28",
+ "hash" : "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81"
+ }
+]
\ No newline at end of file
diff --git a/cmd/evm/testdata/4/alloc.json b/cmd/evm/testdata/4/alloc.json
new file mode 100644
index 0000000000..fadf2bdc4e
--- /dev/null
+++ b/cmd/evm/testdata/4/alloc.json
@@ -0,0 +1,16 @@
+{
+ "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x600340",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/4/env.json b/cmd/evm/testdata/4/env.json
new file mode 100644
index 0000000000..e283eff461
--- /dev/null
+++ b/cmd/evm/testdata/4/env.json
@@ -0,0 +1,8 @@
+{
+ "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "currentDifficulty" : "0x020000",
+ "currentGasLimit" : "0x3b9aca00",
+ "currentNumber" : "0x05",
+ "currentTimestamp" : "0x03e8",
+ "blockHashes" : { "1" : "0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"}
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/4/readme.md b/cmd/evm/testdata/4/readme.md
new file mode 100644
index 0000000000..08840d37bd
--- /dev/null
+++ b/cmd/evm/testdata/4/readme.md
@@ -0,0 +1,3 @@
+These files examplify a transition where a transaction (excuted on block 5) requests
+the blockhash for block `4`, but where the hash for that block is missing.
+It's expected that executing these should cause `exit` with errorcode `4`.
diff --git a/cmd/evm/testdata/4/txs.json b/cmd/evm/testdata/4/txs.json
new file mode 100644
index 0000000000..3044458588
--- /dev/null
+++ b/cmd/evm/testdata/4/txs.json
@@ -0,0 +1,14 @@
+[
+ {
+ "input" : "0x",
+ "gas" : "0x5f5e100",
+ "gasPrice" : "0x1",
+ "nonce" : "0x0",
+ "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "value" : "0x186a0",
+ "v" : "0x1b",
+ "r" : "0x88544c93a564b4c28d2ffac2074a0c55fdd4658fe0d215596ed2e32e3ef7f56b",
+ "s" : "0x7fb4075d54190f825d7c47bb820284757b34fd6293904a93cddb1d3aa961ac28",
+ "hash" : "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81"
+ }
+]
\ No newline at end of file
diff --git a/cmd/evm/testdata/5/alloc.json b/cmd/evm/testdata/5/alloc.json
new file mode 100644
index 0000000000..9e26dfeeb6
--- /dev/null
+++ b/cmd/evm/testdata/5/alloc.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/cmd/evm/testdata/5/env.json b/cmd/evm/testdata/5/env.json
new file mode 100644
index 0000000000..1085f63e62
--- /dev/null
+++ b/cmd/evm/testdata/5/env.json
@@ -0,0 +1,11 @@
+{
+ "currentCoinbase": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ "currentDifficulty": "0x20000",
+ "currentGasLimit": "0x750a163df65e8a",
+ "currentNumber": "1",
+ "currentTimestamp": "1000",
+ "ommers": [
+ {"delta": 1, "address": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" },
+ {"delta": 2, "address": "0xcccccccccccccccccccccccccccccccccccccccc" }
+ ]
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/5/readme.md b/cmd/evm/testdata/5/readme.md
new file mode 100644
index 0000000000..e2b608face
--- /dev/null
+++ b/cmd/evm/testdata/5/readme.md
@@ -0,0 +1 @@
+These files examplify a transition where there are no transcations, two ommers, at block `N-1` (delta 1) and `N-2` (delta 2).
\ No newline at end of file
diff --git a/cmd/evm/testdata/5/txs.json b/cmd/evm/testdata/5/txs.json
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/cmd/evm/testdata/5/txs.json
@@ -0,0 +1 @@
+[]
diff --git a/cmd/evm/testdata/7/alloc.json b/cmd/evm/testdata/7/alloc.json
new file mode 100644
index 0000000000..cef1a25ff0
--- /dev/null
+++ b/cmd/evm/testdata/7/alloc.json
@@ -0,0 +1,12 @@
+{
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0x5ffd4878be161d74",
+ "code": "0x",
+ "nonce": "0xac",
+ "storage": {}
+ },
+ "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{
+ "balance": "0xfeedbead",
+ "nonce" : "0x00"
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/7/env.json b/cmd/evm/testdata/7/env.json
new file mode 100644
index 0000000000..8fd9bc041b
--- /dev/null
+++ b/cmd/evm/testdata/7/env.json
@@ -0,0 +1,7 @@
+{
+ "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "currentDifficulty": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000",
+ "currentGasLimit": "0x750a163df65e8a",
+ "currentNumber": "5",
+ "currentTimestamp": "1000"
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/7/readme.md b/cmd/evm/testdata/7/readme.md
new file mode 100644
index 0000000000..c9826e0ba6
--- /dev/null
+++ b/cmd/evm/testdata/7/readme.md
@@ -0,0 +1,7 @@
+This is a test for HomesteadToDao, checking if the
+DAO-transition works
+
+Example:
+```
+./statet8n --input.alloc=./testdata/7/alloc.json --input.txs=./testdata/7/txs.json --input.env=./testdata/7/env.json --output.alloc=stdout --state.fork=HomesteadToDaoAt5
+```
\ No newline at end of file
diff --git a/cmd/evm/testdata/7/txs.json b/cmd/evm/testdata/7/txs.json
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/cmd/evm/testdata/7/txs.json
@@ -0,0 +1 @@
+[]
diff --git a/cmd/evm/transition-test.sh b/cmd/evm/transition-test.sh
new file mode 100644
index 0000000000..d1400ca577
--- /dev/null
+++ b/cmd/evm/transition-test.sh
@@ -0,0 +1,191 @@
+#!/bin/bash
+ticks="\`\`\`"
+
+function showjson(){
+ echo "\`$1\`:"
+ echo "${ticks}json"
+ cat $1
+ echo ""
+ echo "$ticks"
+}
+function demo(){
+ echo "$ticks"
+ echo "$1"
+ echo "$ticks"
+ echo ""
+}
+function tick(){
+ echo "$ticks"
+}
+
+cat << EOF
+## EVM state transition tool
+
+The \`evm t8n\` tool is a stateless state transition utility. It is a utility
+which can
+
+1. Take a prestate, including
+ - Accounts,
+ - Block context information,
+ - Previous blockshashes (*optional)
+2. Apply a set of transactions,
+3. Apply a mining-reward (*optional),
+4. And generate a post-state, including
+ - State root, transaction root, receipt root,
+ - Information about rejected transactions,
+ - Optionally: a full or partial post-state dump
+
+## Specification
+
+The idea is to specify the behaviour of this binary very _strict_, so that other
+node implementors can build replicas based on their own state-machines, and the
+state generators can swap between a \`geth\`-based implementation and a \`parityvm\`-based
+implementation.
+
+### Command line params
+
+Command line params that has to be supported are
+$(tick)
+
+` ./evm t8n -h | grep "trace\|output\|state\."`
+
+$(tick)
+
+### Error codes and output
+
+All logging should happen against the \`stderr\`.
+There are a few (not many) errors that can occur, those are defined below.
+
+#### EVM-based errors (\`2\` to \`9\`)
+
+- Other EVM error. Exit code \`2\`
+- Failed configuration: when a non-supported or invalid fork was specified. Exit code \`3\`.
+- Block history is not supplied, but needed for a \`BLOCKHASH\` operation. If \`BLOCKHASH\`
+ is invoked targeting a block which history has not been provided for, the program will
+ exit with code \`4\`.
+
+#### IO errors (\`10\`-\`20\`)
+
+- Invalid input json: the supplied data could not be marshalled.
+ The program will exit with code \`10\`
+- IO problems: failure to load or save files, the program will exit with code \`11\`
+
+EOF
+
+# This should exit with 3
+./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --state.fork=Frontier+1346 2>/dev/null
+if [ $? != 3 ]; then
+ echo "Failed, exitcode should be 3"
+fi
+cat << EOF
+## Examples
+### Basic usage
+
+Invoking it with the provided example files
+EOF
+cmd="./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json"
+tick;echo "$cmd"; tick
+$cmd 2>/dev/null
+echo "Two resulting files:"
+echo ""
+showjson alloc.json
+showjson result.json
+echo ""
+
+echo "We can make them spit out the data to e.g. \`stdout\` like this:"
+cmd="./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.result=stdout --output.alloc=stdout"
+tick;echo "$cmd"; tick
+output=`$cmd 2>/dev/null`
+echo "Output:"
+echo "${ticks}json"
+echo "$output"
+echo "$ticks"
+
+cat << EOF
+
+## About Ommers
+
+Mining rewards and ommer rewards might need to be added. This is how those are applied:
+
+- \`block_reward\` is the block mining reward for the miner (\`0xaa\`), of a block at height \`N\`.
+- For each ommer (mined by \`0xbb\`), with blocknumber \`N-delta\`
+ - (where \`delta\` is the difference between the current block and the ommer)
+ - The account \`0xbb\` (ommer miner) is awarded \`(8-delta)/ 8 * block_reward\`
+ - The account \`0xaa\` (block miner) is awarded \`block_reward / 32\`
+
+To make \`state_t8n\` apply these, the following inputs are required:
+
+- \`state.reward\`
+ - For ethash, it is \`5000000000000000000\` \`wei\`,
+ - If this is not defined, mining rewards are not applied,
+ - A value of \`0\` is valid, and causes accounts to be 'touched'.
+- For each ommer, the tool needs to be given an \`address\` and a \`delta\`. This
+ is done via the \`env\`.
+
+Note: the tool does not verify that e.g. the normal uncle rules apply,
+and allows e.g two uncles at the same height, or the uncle-distance. This means that
+the tool allows for negative uncle reward (distance > 8)
+
+Example:
+EOF
+
+showjson ./testdata/5/env.json
+
+echo "When applying this, using a reward of \`0x08\`"
+cmd="./evm t8n --input.alloc=./testdata/5/alloc.json -input.txs=./testdata/5/txs.json --input.env=./testdata/5/env.json --output.alloc=stdout --state.reward=0x80"
+output=`$cmd 2>/dev/null`
+echo "Output:"
+echo "${ticks}json"
+echo "$output"
+echo "$ticks"
+
+echo "### Future EIPS"
+echo ""
+echo "It is also possible to experiment with future eips that are not yet defined in a hard fork."
+echo "Example, putting EIP-1344 into Frontier: "
+cmd="./evm t8n --state.fork=Frontier+1344 --input.pre=./testdata/1/pre.json --input.txs=./testdata/1/txs.json --input.env=/testdata/1/env.json"
+tick;echo "$cmd"; tick
+echo ""
+
+echo "### Block history"
+echo ""
+echo "The \`BLOCKHASH\` opcode requires blockhashes to be provided by the caller, inside the \`env\`."
+echo "If a required blockhash is not provided, the exit code should be \`4\`:"
+echo "Example where blockhashes are provided: "
+cmd="./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace"
+tick && echo $cmd && tick
+$cmd 2>&1 >/dev/null
+cmd="cat trace-0.jsonl | grep BLOCKHASH -C2"
+tick && echo $cmd && tick
+echo "$ticks"
+cat trace-0.jsonl | grep BLOCKHASH -C2
+echo "$ticks"
+echo ""
+
+echo "In this example, the caller has not provided the required blockhash:"
+cmd="./evm t8n --input.alloc=./testdata/4/alloc.json --input.txs=./testdata/4/txs.json --input.env=./testdata/4/env.json --trace"
+tick && echo $cmd && tick
+tick
+$cmd
+errc=$?
+tick
+echo "Error code: $errc"
+
+
+echo "### Chaining"
+echo ""
+echo "Another thing that can be done, is to chain invocations:"
+cmd1="./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout"
+cmd2="./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json"
+echo "$ticks"
+echo "$cmd1 | $cmd2"
+output=$($cmd1 | $cmd2 )
+echo $output
+echo "$ticks"
+echo "What happened here, is that we first applied two identical transactions, so the second one was rejected. "
+echo "Then, taking the poststate alloc as the input for the next state, we tried again to include"
+echo "the same two transactions: this time, both failed due to too low nonce."
+echo ""
+echo "In order to meaningfully chain invocations, one would need to provide meaningful new \`env\`, otherwise the"
+echo "actual blocknumber (exposed to the EVM) would not increase."
+echo ""
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index cd5eca3531..85bb0b78f9 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -699,7 +699,8 @@ func authTwitter(url string) (string, string, common.Address, error) {
// the mobile page though since the main page loads tweet contents via JS.
// #nosec (we don't use faucet)
url = strings.Replace(url, "https://twitter.com/", "https://mobile.twitter.com/", 1)
- //nolint:gosec G107 this function is called with a twitter url
+ // gosec/G107. This function is called with a twitter url, no need to run gosec.
+ //nolint:gosec
res, err := http.Get(url)
if err != nil {
return "", "", common.Address{}, err
diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go
index 598790a306..3ed676d5ec 100644
--- a/cmd/geth/accountcmd.go
+++ b/cmd/geth/accountcmd.go
@@ -26,7 +26,6 @@ import (
"github.com/celo-org/celo-blockchain/accounts/usbwallet"
"github.com/celo-org/celo-blockchain/cmd/utils"
"github.com/celo-org/celo-blockchain/common"
- prompt2 "github.com/celo-org/celo-blockchain/console/prompt"
"github.com/celo-org/celo-blockchain/crypto"
"github.com/celo-org/celo-blockchain/log"
cli "gopkg.in/urfave/cli.v1"
@@ -382,7 +381,7 @@ func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []str
}
for trials := 0; trials < 3; trials++ {
prompt := fmt.Sprintf("Unlocking account %s | Attempt %d/%d", address, trials+1, 3)
- password := getPassPhrase(prompt, false, i, passwords)
+ password := utils.GetPassPhraseWithList(prompt, false, i, passwords)
err = ks.Unlock(account, password)
if err == nil {
log.Info("Unlocked account", "address", account.Address.Hex())
@@ -403,36 +402,6 @@ func unlockAccount(ks *keystore.KeyStore, address string, i int, passwords []str
return accounts.Account{}, ""
}
-// getPassPhrase retrieves the password associated with an account, either fetched
-// from a list of preloaded passphrases, or requested interactively from the user.
-func getPassPhrase(prompt string, confirmation bool, i int, passwords []string) string {
- // If a list of passwords was supplied, retrieve from them
- if len(passwords) > 0 {
- if i < len(passwords) {
- return passwords[i]
- }
- return passwords[len(passwords)-1]
- }
- // Otherwise prompt the user for the password
- if prompt != "" {
- fmt.Println(prompt)
- }
- password, err := prompt2.Stdin.PromptPassword("Password: ")
- if err != nil {
- utils.Fatalf("Failed to read password: %v", err)
- }
- if confirmation {
- confirm, err := prompt2.Stdin.PromptPassword("Repeat password: ")
- if err != nil {
- utils.Fatalf("Failed to read password confirmation: %v", err)
- }
- if password != confirm {
- utils.Fatalf("Passwords do not match")
- }
- }
- return password
-}
-
func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrError, auth string) accounts.Account {
fmt.Printf("Multiple key files exist for address %x:\n", err.Addr)
for _, a := range err.Matches {
@@ -475,7 +444,7 @@ func accountCreate(ctx *cli.Context) error {
utils.Fatalf("Failed to read configuration: %v", err)
}
- password := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
+ password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
account, err := keystore.StoreKey(keydir, password, scryptN, scryptP)
@@ -503,7 +472,7 @@ func accountUpdate(ctx *cli.Context) error {
for _, addr := range ctx.Args() {
account, oldPassword := unlockAccount(ks, addr, 0, nil)
- newPassword := getPassPhrase("Please give a new password. Do not forget this password.", true, 0, nil)
+ newPassword := utils.GetPassPhraseWithList("Please give a new password. Do not forget this password.", true, 0, nil)
if err := ks.Update(account, oldPassword, newPassword); err != nil {
utils.Fatalf("Could not update the account: %v", err)
}
@@ -522,7 +491,7 @@ func importWallet(ctx *cli.Context) error {
}
stack, _ := makeConfigNode(ctx)
- passphrase := getPassPhrase("", false, 0, utils.MakePasswordList(ctx))
+ passphrase := utils.GetPassPhraseWithList("", false, 0, utils.MakePasswordList(ctx))
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
acct, err := ks.ImportPreSaleKey(keyJSON, passphrase)
@@ -543,7 +512,7 @@ func accountImport(ctx *cli.Context) error {
utils.Fatalf("Failed to load the private key: %v", err)
}
stack, _ := makeConfigNode(ctx)
- passphrase := getPassPhrase("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
+ passphrase := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
acct, err := ks.ImportECDSA(key, passphrase)
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index e1ac309d1c..5d9b404521 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -87,6 +87,8 @@ The dumpgenesis command dumps the genesis block configuration in JSON format to
utils.CacheGCFlag,
utils.MetricsEnabledFlag,
utils.MetricsEnabledExpensiveFlag,
+ utils.MetricsHTTPFlag,
+ utils.MetricsPortFlag,
utils.MetricsEnableInfluxDBFlag,
utils.MetricsInfluxDBEndpointFlag,
utils.MetricsInfluxDBDatabaseFlag,
@@ -305,8 +307,10 @@ func importChain(ctx *cli.Context) error {
// Import the chain
start := time.Now()
+ var importErr error
for _, arg := range ctx.Args() {
if err := utils.ImportChain(chain, arg); err != nil {
+ importErr = err
log.Error("Import error", "file", arg, "err", err)
}
}
@@ -358,7 +362,7 @@ func importChain(ctx *cli.Context) error {
utils.Fatalf("Failed to read database iostats: %v", err)
}
fmt.Println(ioStats)
- return nil
+ return importErr
}
func exportChain(ctx *cli.Context) error {
diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go
index 730fc612fa..9a1671721f 100644
--- a/cmd/geth/consolecmd_test.go
+++ b/cmd/geth/consolecmd_test.go
@@ -104,7 +104,7 @@ func TestHTTPAttachWelcome(t *testing.T) {
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--light.maxpeers", "0",
- "--etherbase", coinbase, "--rpc", "--rpcport", port)
+ "--etherbase", coinbase, "--http", "--http.port", port)
defer func() {
geth.Interrupt()
geth.ExpectExit()
@@ -121,7 +121,7 @@ func TestWSAttachWelcome(t *testing.T) {
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--light.maxpeers", "0",
- "--etherbase", coinbase, "--ws", "--wsport", port)
+ "--etherbase", coinbase, "--ws", "--ws.port", port)
defer func() {
geth.Interrupt()
geth.ExpectExit()
diff --git a/cmd/geth/les_test.go b/cmd/geth/les_test.go
new file mode 100644
index 0000000000..1f517219cc
--- /dev/null
+++ b/cmd/geth/les_test.go
@@ -0,0 +1,147 @@
+package main
+
+import (
+ "context"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/celo-org/celo-blockchain/p2p"
+ "github.com/celo-org/celo-blockchain/rpc"
+)
+
+type gethrpc struct {
+ name string
+ rpc *rpc.Client
+ geth *testgeth
+ nodeInfo *p2p.NodeInfo
+}
+
+func (g *gethrpc) killAndWait() {
+ g.geth.Kill()
+ g.geth.WaitExit()
+}
+
+func (g *gethrpc) callRPC(result interface{}, method string, args ...interface{}) {
+ if err := g.rpc.Call(&result, method, args...); err != nil {
+ g.geth.Fatalf("callRPC %v: %v", method, err)
+ }
+}
+
+func (g *gethrpc) addPeer(peer *gethrpc) {
+ g.geth.Logf("%v.addPeer(%v)", g.name, peer.name)
+ enode := peer.getNodeInfo().Enode
+ peerCh := make(chan *p2p.PeerEvent)
+ sub, err := g.rpc.Subscribe(context.Background(), "admin", peerCh, "peerEvents")
+ if err != nil {
+ g.geth.Fatalf("subscribe %v: %v", g.name, err)
+ }
+ defer sub.Unsubscribe()
+ g.callRPC(nil, "admin_addPeer", enode)
+ dur := 14 * time.Second
+ timeout := time.After(dur)
+ select {
+ case ev := <-peerCh:
+ g.geth.Logf("%v received event: type=%v, peer=%v", g.name, ev.Type, ev.Peer)
+ case err := <-sub.Err():
+ g.geth.Fatalf("%v sub error: %v", g.name, err)
+ case <-timeout:
+ g.geth.Error("timeout adding peer after", dur)
+ }
+}
+
+// Use this function instead of `g.nodeInfo` directly
+func (g *gethrpc) getNodeInfo() *p2p.NodeInfo {
+ if g.nodeInfo != nil {
+ return g.nodeInfo
+ }
+ g.nodeInfo = &p2p.NodeInfo{}
+ g.callRPC(&g.nodeInfo, "admin_nodeInfo")
+ return g.nodeInfo
+}
+
+func startGethWithRpc(t *testing.T, name string, args ...string) *gethrpc {
+ g := &gethrpc{name: name}
+ args = append([]string{"--networkid=42", "--port=0", "--nousb", "--http", "--http.port=0", "--http.api=admin,eth,les"}, args...)
+ t.Logf("Starting %v with rpc: %v", name, args)
+ g.geth = runGeth(t, args...)
+ // wait before we can attach to it. TODO: probe for it properly
+ time.Sleep(1 * time.Second)
+ var err error
+ ipcpath := filepath.Join(g.geth.Datadir, "geth.ipc")
+ g.rpc, err = rpc.Dial(ipcpath)
+ if err != nil {
+ t.Fatalf("%v rpc connect: %v", name, err)
+ }
+ return g
+}
+
+func initGeth(t *testing.T) string {
+ g := runGeth(t, "--networkid=42", "init", "./testdata/clique.json")
+ datadir := g.Datadir
+ g.WaitExit()
+ return datadir
+}
+
+func startLightServer(t *testing.T) *gethrpc {
+ datadir := initGeth(t)
+ runGeth(t, "--datadir", datadir, "--password", "./testdata/password.txt", "account", "import", "./testdata/key.prv").WaitExit()
+ account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
+ server := startGethWithRpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt",
+ "--unlock", account, "--mine", "--miner.validator", account, "--tx-fee-recipient", account,
+ "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1")
+ return server
+}
+
+func startClient(t *testing.T, name string) *gethrpc {
+ datadir := initGeth(t)
+ return startGethWithRpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1")
+}
+
+func TestPriorityClient(t *testing.T) {
+ lightServer := startLightServer(t)
+ defer lightServer.killAndWait()
+
+ // Start client and add lightServer as peer
+ freeCli := startClient(t, "freeCli")
+ defer freeCli.killAndWait()
+ freeCli.addPeer(lightServer)
+
+ var peers []*p2p.PeerInfo
+ freeCli.callRPC(&peers, "admin_peers")
+ if len(peers) != 1 {
+ t.Errorf("Expected: # of client peers == 1, actual: %v", len(peers))
+ return
+ }
+
+ // Set up priority client, get its nodeID, increase its balance on the lightServer
+ prioCli := startClient(t, "prioCli")
+ defer prioCli.killAndWait()
+ // 3_000_000_000 once we move to Go 1.13
+ tokens := 3000000000
+ lightServer.callRPC(nil, "les_addBalance", prioCli.getNodeInfo().ID, tokens, "foobar")
+ prioCli.addPeer(lightServer)
+
+ // Check if priority client is actually syncing and the regular client got kicked out
+ prioCli.callRPC(&peers, "admin_peers")
+ if len(peers) != 1 {
+ t.Errorf("Expected: # of prio peers == 1, actual: %v", len(peers))
+ }
+
+ nodes := map[string]*gethrpc{
+ lightServer.getNodeInfo().ID: lightServer,
+ freeCli.getNodeInfo().ID: freeCli,
+ prioCli.getNodeInfo().ID: prioCli,
+ }
+ lightServer.callRPC(&peers, "admin_peers")
+ peersWithNames := make(map[string]string)
+ for _, p := range peers {
+ peersWithNames[nodes[p.ID].name] = p.ID
+ }
+ if _, freeClientFound := peersWithNames[freeCli.name]; freeClientFound {
+ t.Error("client is still a peer of lightServer", peersWithNames)
+ }
+ if _, prioClientFound := peersWithNames[prioCli.name]; !prioClientFound {
+ t.Error("prio client is not among lightServer peers", peersWithNames)
+ }
+}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index ea8105598e..be20f66493 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -182,6 +182,7 @@ var (
utils.IPCPathFlag,
utils.InsecureUnlockAllowedFlag,
utils.RPCGlobalGasCap,
+ utils.RPCGlobalTxFeeCap,
}
whisperFlags = []cli.Flag{
@@ -194,6 +195,8 @@ var (
metricsFlags = []cli.Flag{
utils.MetricsEnabledFlag,
utils.MetricsEnabledExpensiveFlag,
+ utils.MetricsHTTPFlag,
+ utils.MetricsPortFlag,
utils.MetricsEnableInfluxDBFlag,
utils.MetricsInfluxDBEndpointFlag,
utils.MetricsInfluxDBDatabaseFlag,
diff --git a/cmd/geth/testdata/blockchain.blocks b/cmd/geth/testdata/blockchain.blocks
new file mode 100644
index 0000000000..d29453d3e5
Binary files /dev/null and b/cmd/geth/testdata/blockchain.blocks differ
diff --git a/cmd/geth/testdata/clique.json b/cmd/geth/testdata/clique.json
new file mode 100644
index 0000000000..00d1e6666a
--- /dev/null
+++ b/cmd/geth/testdata/clique.json
@@ -0,0 +1,22 @@
+{
+ "config": {
+ "chainId": 15,
+ "homesteadBlock": 0,
+ "eip150Block": 0,
+ "eip155Block": 0,
+ "eip158Block": 0,
+ "byzantiumBlock": 0,
+ "constantinopleBlock": 0,
+ "petersburgBlock": 0,
+ "istanbul": {
+ }
+ },
+ "difficulty": "1",
+ "gasLimit": "8000000",
+ "extraData": "0xecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753ef90262f869945296a071434eb2943c1bde8a2e1d555d911cd1d594bc236d14fbbe74b4d3bc132309b861dcf1c527c594ab3233a00d1e03b3e0c7c9912421b56f498901ef9421d87e9445d3b355807c91937e70e6f9914f2a1d94f6a964ad845a8a8a4a50b884c140ab8c3ed8252cf901eab86056631e24a6ec7fb89b5d967b6e7169eed00c0d802b9050d5a749b9d69a2c70d047b5add1d78646c4c3d579e7da7691001f0c832e9b26dd02b960b610ebfa63cd3a9dc0d6da8abfd8c16ffc5679debc682216c2934af7dc93d82e4a42564b4d80b86065be0e12d1ac62a4ddf326cb0dde804d8c1aab376dff994c9db706c6a5c0d5ba57319105491658f9ae5d5a4f48752b006137148123d8a526b2237d7284d5f0ab664f186ef0e7649d586a9754f54ca042539b3ba7ea25d0209f639cce30262081b860f1dade6f52a125a236a546a5bf5468ef8c95da980a51ee2ea595919e80bb56b3941bd17315b2681a411b6f7b6a2aaa01815a62fff57a14cae0cbef5a540dbd34f098ae18c07f93137eefc25132ac1971c8e74f2ddf24ceeeece87dcd18f19500b8601e4c01b96874cbc25fe98a4a8300035865d02724ec7b62d1662eba6b49777aaad51b73eb228d136d8d1f436e391e7e01d2c597cfcc17465a3aa1951d610360365bb116ab759887d5a74064c663aeeb3facf55ddc9a212e9f06b9925d27011700b860306ff63a1746b45b891a33d8ad7a02c92bf0ec628499f5308c55b15e7b5a2ab3d97113ef669c5ae446cb69b965c28d0056c780a4f2462b989bcb15380cc71fcf163d7e7be97c8c2a70b72e8273ff87f7d249f3c552fe7ecc28331f4ce90d92808080c3808080c3808080",
+ "alloc": {
+ "02f0d131f1f97aef08aec6e3291b957d9efe7105": {
+ "balance": "300000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/cmd/geth/testdata/key.prv b/cmd/geth/testdata/key.prv
new file mode 100644
index 0000000000..1d2687ea63
--- /dev/null
+++ b/cmd/geth/testdata/key.prv
@@ -0,0 +1 @@
+48aa455c373ec5ce7fefb0e54f44a215decdc85b9047bc4d09801e038909bdbe
\ No newline at end of file
diff --git a/cmd/geth/testdata/password.txt b/cmd/geth/testdata/password.txt
new file mode 100644
index 0000000000..f6ea049518
--- /dev/null
+++ b/cmd/geth/testdata/password.txt
@@ -0,0 +1 @@
+foobar
\ No newline at end of file
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index 436e3ed15c..a09b81cbd1 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -162,6 +162,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.GraphQLCORSDomainFlag,
utils.GraphQLVirtualHostsFlag,
utils.RPCGlobalGasCap,
+ utils.RPCGlobalTxFeeCap,
utils.JSpathFlag,
utils.ExecFlag,
utils.PreloadJSFlag,
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index d110c4d9e8..c1a9b23db3 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -23,7 +23,6 @@ import (
"fmt"
"io"
"io/ioutil"
- "math/big"
"os"
"path/filepath"
"strconv"
@@ -49,6 +48,7 @@ import (
"github.com/celo-org/celo-blockchain/les"
"github.com/celo-org/celo-blockchain/log"
"github.com/celo-org/celo-blockchain/metrics"
+ "github.com/celo-org/celo-blockchain/metrics/exp"
"github.com/celo-org/celo-blockchain/metrics/influxdb"
"github.com/celo-org/celo-blockchain/miner"
"github.com/celo-org/celo-blockchain/node"
@@ -68,8 +68,8 @@ var (
{{if .cmd.Description}}{{.cmd.Description}}
{{end}}{{if .cmd.Subcommands}}
SUBCOMMANDS:
- {{range .cmd.Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
- {{end}}{{end}}{{if .categorizedFlags}}
+ {{range .cmd.Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
+ {{end}}{{end}}{{if .categorizedFlags}}
{{range $idx, $categorized := .categorizedFlags}}{{$categorized.Name}} OPTIONS:
{{range $categorized.Flags}}{{"\t"}}{{.}}
{{end}}
@@ -79,10 +79,10 @@ SUBCOMMANDS:
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
- {{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
- {{end}}{{end}}{{if .Flags}}
+ {{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
+ {{end}}{{end}}{{if .Flags}}
OPTIONS:
-{{range $.Flags}}{{"\t"}}{{.}}
+{{range $.Flags}} {{.}}
{{end}}
{{end}}`
)
@@ -430,9 +430,14 @@ var (
}
RPCGlobalGasCap = cli.Uint64Flag{
Name: "rpc.gascap",
- Usage: "Sets a cap on gas that can be used in eth_call/estimateGas",
+ Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)",
+ Value: eth.DefaultConfig.RPCGasCap,
+ }
+ RPCGlobalTxFeeCap = cli.Float64Flag{
+ Name: "rpc.txfeecap",
+ Usage: "Sets a cap on transaction fee (in celo) that can be sent via the RPC APIs (0 = no cap)",
+ Value: eth.DefaultConfig.RPCTxFeeCap,
}
-
// Logging and debug settings
CeloStatsURLFlag = cli.StringFlag{
@@ -640,6 +645,21 @@ var (
Name: "metrics.expensive",
Usage: "Enable expensive metrics collection and reporting",
}
+
+ // MetricsHTTPFlag defines the endpoint for a stand-alone metrics HTTP endpoint.
+ // Since the pprof service enables sensitive/vulnerable behavior, this allows a user
+ // to enable a public-OK metrics endpoint without having to worry about ALSO exposing
+ // other profiling behavior or information.
+ MetricsHTTPFlag = cli.StringFlag{
+ Name: "metrics.addr",
+ Usage: "Enable stand-alone metrics HTTP server listening interface",
+ Value: "127.0.0.1",
+ }
+ MetricsPortFlag = cli.IntFlag{
+ Name: "metrics.port",
+ Usage: "Metrics HTTP server listening port",
+ Value: 6060,
+ }
MetricsEnableInfluxDBFlag = cli.BoolFlag{
Name: "metrics.influxdb",
Usage: "Enable metrics export/push to an external InfluxDB database",
@@ -881,12 +901,15 @@ func setNAT(ctx *cli.Context, cfg *p2p.Config) {
// splitAndTrim splits input separated by a comma
// and trims excessive white space from the substrings.
-func splitAndTrim(input string) []string {
- result := strings.Split(input, ",")
- for i, r := range result {
- result[i] = strings.TrimSpace(r)
+func splitAndTrim(input string) (ret []string) {
+ l := strings.Split(input, ",")
+ for _, r := range l {
+ r = strings.TrimSpace(r)
+ if len(r) > 0 {
+ ret = append(ret, r)
+ }
}
- return result
+ return ret
}
// setHTTP creates the HTTP RPC listener interface string from the set
@@ -1658,8 +1681,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
}
if ctx.GlobalIsSet(RPCGlobalGasCap.Name) {
- cfg.RPCGasCap = new(big.Int).SetUint64(ctx.GlobalUint64(RPCGlobalGasCap.Name))
+ cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCap.Name)
+ }
+ if cfg.RPCGasCap != 0 {
+ log.Info("Set global gas cap", "cap", cfg.RPCGasCap)
+ } else {
+ log.Info("Global gas cap disabled")
+ }
+
+ if ctx.GlobalIsSet(RPCGlobalTxFeeCap.Name) {
+ cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCap.Name)
}
+
// Disable DNS discovery by default (by using the flag's value even if it hasn't been set and so
// has the default value ""), since we don't have DNS discovery set up for Celo.
// Note that passing --discovery.dns "" is the way the Geth docs specify for disabling DNS discovery,
@@ -1804,6 +1837,7 @@ func RegisterGraphQLService(stack *node.Node, endpoint string, cors, vhosts []st
func SetupMetrics(ctx *cli.Context) {
if metrics.Enabled {
log.Info("Enabling metrics collection")
+
var (
enableExport = ctx.GlobalBool(MetricsEnableInfluxDBFlag.Name)
endpoint = ctx.GlobalString(MetricsInfluxDBEndpointFlag.Name)
@@ -1819,6 +1853,12 @@ func SetupMetrics(ctx *cli.Context) {
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap)
}
+
+ if ctx.GlobalIsSet(MetricsHTTPFlag.Name) {
+ address := fmt.Sprintf("%s:%d", ctx.GlobalString(MetricsHTTPFlag.Name), ctx.GlobalInt(MetricsPortFlag.Name))
+ log.Info("Enabling stand-alone metrics HTTP endpoint", "address", address)
+ exp.Setup(address)
+ }
}
}
diff --git a/cmd/utils/prompt.go b/cmd/utils/prompt.go
new file mode 100644
index 0000000000..20ecd55f69
--- /dev/null
+++ b/cmd/utils/prompt.go
@@ -0,0 +1,62 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package utils contains internal helper functions for go-ethereum commands.
+package utils
+
+import (
+ "fmt"
+
+ "github.com/celo-org/celo-blockchain/console/prompt"
+)
+
+// GetPassPhrase displays the given text(prompt) to the user and requests some textual
+// data to be entered, but one which must not be echoed out into the terminal.
+// The method returns the input provided by the user.
+func GetPassPhrase(text string, confirmation bool) string {
+ if text != "" {
+ fmt.Println(text)
+ }
+ password, err := prompt.Stdin.PromptPassword("Password: ")
+ if err != nil {
+ Fatalf("Failed to read password: %v", err)
+ }
+ if confirmation {
+ confirm, err := prompt.Stdin.PromptPassword("Repeat password: ")
+ if err != nil {
+ Fatalf("Failed to read password confirmation: %v", err)
+ }
+ if password != confirm {
+ Fatalf("Passwords do not match")
+ }
+ }
+ return password
+}
+
+// GetPassPhraseWithList retrieves the password associated with an account, either fetched
+// from a list of preloaded passphrases, or requested interactively from the user.
+func GetPassPhraseWithList(text string, confirmation bool, index int, passwords []string) string {
+ // If a list of passwords was supplied, retrieve from them
+ if len(passwords) > 0 {
+ if index < len(passwords) {
+ return passwords[index]
+ }
+ return passwords[len(passwords)-1]
+ }
+ // Otherwise prompt the user for the password
+ password := GetPassPhrase(text, confirmation)
+ return password
+}
diff --git a/cmd/utils/prompt_test.go b/cmd/utils/prompt_test.go
new file mode 100644
index 0000000000..62ea75a3f6
--- /dev/null
+++ b/cmd/utils/prompt_test.go
@@ -0,0 +1,74 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package utils contains internal helper functions for go-ethereum commands.
+package utils
+
+import (
+ "testing"
+)
+
+func TestGetPassPhraseWithList(t *testing.T) {
+ type args struct {
+ text string
+ confirmation bool
+ index int
+ passwords []string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ "test1",
+ args{
+ "text1",
+ false,
+ 0,
+ []string{"zero", "one", "two"},
+ },
+ "zero",
+ },
+ {
+ "test2",
+ args{
+ "text2",
+ false,
+ 5,
+ []string{"zero", "one", "two"},
+ },
+ "two",
+ },
+ {
+ "test3",
+ args{
+ "text3",
+ true,
+ 1,
+ []string{"zero", "one", "two"},
+ },
+ "one",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := GetPassPhraseWithList(tt.args.text, tt.args.confirmation, tt.args.index, tt.args.passwords); got != tt.want {
+ t.Errorf("GetPassPhraseWithList() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/common/fdlimit/fdlimit_freebsd.go b/common/fdlimit/fdlimit_bsd.go
similarity index 96%
rename from common/fdlimit/fdlimit_freebsd.go
rename to common/fdlimit/fdlimit_bsd.go
index 0d8727138e..86181337a2 100644
--- a/common/fdlimit/fdlimit_freebsd.go
+++ b/common/fdlimit/fdlimit_bsd.go
@@ -14,14 +14,14 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// +build freebsd
+// +build freebsd dragonfly
package fdlimit
import "syscall"
// This file is largely identical to fdlimit_unix.go,
-// but Rlimit fields have type int64 on FreeBSD so it needs
+// but Rlimit fields have type int64 on *BSD so it needs
// an extra conversion.
// Raise tries to maximize the file descriptor allowance of this process
diff --git a/common/math/integer.go b/common/math/integer.go
index 93b1d036dd..50d3eba1f5 100644
--- a/common/math/integer.go
+++ b/common/math/integer.go
@@ -18,6 +18,7 @@ package math
import (
"fmt"
+ "math/bits"
"strconv"
)
@@ -78,22 +79,20 @@ func MustParseUint64(s string) uint64 {
return v
}
-// NOTE: The following methods need to be optimised using either bit checking or asm
-
-// SafeSub returns subtraction result and whether overflow occurred.
+// SafeSub returns x-y and checks for overflow.
func SafeSub(x, y uint64) (uint64, bool) {
- return x - y, x < y
+ diff, borrowOut := bits.Sub64(x, y, 0)
+ return diff, borrowOut != 0
}
-// SafeAdd returns the result and whether overflow occurred.
+// SafeAdd returns x+y and checks for overflow.
func SafeAdd(x, y uint64) (uint64, bool) {
- return x + y, y > MaxUint64-x
+ sum, carryOut := bits.Add64(x, y, 0)
+ return sum, carryOut != 0
}
-// SafeMul returns multiplication result and whether overflow occurred.
+// SafeMul returns x*y and checks for overflow.
func SafeMul(x, y uint64) (uint64, bool) {
- if x == 0 || y == 0 {
- return 0, false
- }
- return x * y, y > MaxUint64/x
+ hi, lo := bits.Mul64(x, y)
+ return lo, hi != 0
}
diff --git a/console/console.go b/console/console.go
index 0a36b83282..2a1112b406 100644
--- a/console/console.go
+++ b/console/console.go
@@ -39,7 +39,8 @@ import (
)
var (
- passwordRegexp = regexp.MustCompile(`personal.[nus]`)
+ // u: unlock, s: signXX, sendXX, n: newAccount, i: importXX
+ passwordRegexp = regexp.MustCompile(`personal.[nusi]`)
onlyWhitespace = regexp.MustCompile(`^\s*$`)
exit = regexp.MustCompile(`^\s*exit\s*;*\s*$`)
)
diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go
index 3d4bfc6f7a..597c082079 100644
--- a/core/rawdb/freezer.go
+++ b/core/rawdb/freezer.go
@@ -333,7 +333,7 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
var (
start = time.Now()
first = f.frozen
- ancients = make([]common.Hash, 0, limit)
+ ancients = make([]common.Hash, 0, limit-f.frozen)
)
for f.frozen <= limit {
// Retrieves all the components of the canonical block
diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go
index e14c6b920c..93e730fb30 100644
--- a/core/rawdb/freezer_table.go
+++ b/core/rawdb/freezer_table.go
@@ -232,8 +232,8 @@ func (t *freezerTable) repair() error {
t.index.ReadAt(buffer, 0)
firstIndex.unmarshalBinary(buffer)
- t.tailId = firstIndex.offset
- t.itemOffset = firstIndex.filenum
+ t.tailId = firstIndex.filenum
+ t.itemOffset = firstIndex.offset
t.index.ReadAt(buffer, offsetsSize-indexEntrySize)
lastIndex.unmarshalBinary(buffer)
@@ -519,16 +519,27 @@ func (t *freezerTable) Append(item uint64, blob []byte) error {
// getBounds returns the indexes for the item
// returns start, end, filenumber and error
func (t *freezerTable) getBounds(item uint64) (uint32, uint32, uint32, error) {
- var startIdx, endIdx indexEntry
buffer := make([]byte, indexEntrySize)
- if _, err := t.index.ReadAt(buffer, int64(item*indexEntrySize)); err != nil {
- return 0, 0, 0, err
- }
- startIdx.unmarshalBinary(buffer)
+ var startIdx, endIdx indexEntry
+ // Read second index
if _, err := t.index.ReadAt(buffer, int64((item+1)*indexEntrySize)); err != nil {
return 0, 0, 0, err
}
endIdx.unmarshalBinary(buffer)
+ // Read first index (unless it's the very first item)
+ if item != 0 {
+ if _, err := t.index.ReadAt(buffer, int64(item*indexEntrySize)); err != nil {
+ return 0, 0, 0, err
+ }
+ startIdx.unmarshalBinary(buffer)
+ } else {
+ // Special case if we're reading the first item in the freezer. We assume that
+ // the first item always start from zero(regarding the deletion, we
+ // only support deletion by files, so that the assumption is held).
+ // This means we can use the first item metadata to carry information about
+ // the 'global' offset, for the deletion-case
+ return 0, endIdx.offset, endIdx.filenum, nil
+ }
if startIdx.filenum != endIdx.filenum {
// If a piece of data 'crosses' a data-file,
// it's actually in one piece on the second data-file.
diff --git a/core/rawdb/freezer_table_test.go b/core/rawdb/freezer_table_test.go
index 3ebfad4e3d..55cf7adb9e 100644
--- a/core/rawdb/freezer_table_test.go
+++ b/core/rawdb/freezer_table_test.go
@@ -564,8 +564,8 @@ func TestOffset(t *testing.T) {
tailId := uint32(2) // First file is 2
itemOffset := uint32(4) // We have removed four items
zeroIndex := indexEntry{
- offset: tailId,
- filenum: itemOffset,
+ filenum: tailId,
+ offset: itemOffset,
}
buf := zeroIndex.marshallBinary()
// Overwrite index zero
@@ -579,39 +579,67 @@ func TestOffset(t *testing.T) {
}
// Now open again
- {
+ checkPresent := func(numDeleted uint64) {
f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 40, true)
if err != nil {
t.Fatal(err)
}
f.printIndex()
// It should allow writing item 6
- f.Append(6, getChunk(20, 0x99))
+ f.Append(numDeleted+2, getChunk(20, 0x99))
// It should be fine to fetch 4,5,6
- if got, err := f.Retrieve(4); err != nil {
+ if got, err := f.Retrieve(numDeleted); err != nil {
t.Fatal(err)
} else if exp := getChunk(20, 0xbb); !bytes.Equal(got, exp) {
t.Fatalf("expected %x got %x", exp, got)
}
- if got, err := f.Retrieve(5); err != nil {
+ if got, err := f.Retrieve(numDeleted + 1); err != nil {
t.Fatal(err)
} else if exp := getChunk(20, 0xaa); !bytes.Equal(got, exp) {
t.Fatalf("expected %x got %x", exp, got)
}
- if got, err := f.Retrieve(6); err != nil {
+ if got, err := f.Retrieve(numDeleted + 2); err != nil {
t.Fatal(err)
} else if exp := getChunk(20, 0x99); !bytes.Equal(got, exp) {
t.Fatalf("expected %x got %x", exp, got)
}
// It should error at 0, 1,2,3
- for i := 0; i < 4; i++ {
- if _, err := f.Retrieve(uint64(i)); err == nil {
+ for i := numDeleted - 1; i > numDeleted-10; i-- {
+ if _, err := f.Retrieve(i); err == nil {
t.Fatal("expected err")
}
}
}
+ checkPresent(4)
+ // Now, let's pretend we have deleted 1M items
+ {
+ // Read the index file
+ p := filepath.Join(os.TempDir(), fmt.Sprintf("%v.ridx", fname))
+ indexFile, err := os.OpenFile(p, os.O_RDWR, 0644)
+ if err != nil {
+ t.Fatal(err)
+ }
+ indexBuf := make([]byte, 3*indexEntrySize)
+ indexFile.Read(indexBuf)
+
+ // Update the index file, so that we store
+ // [ file = 2, offset = 1M ] at index zero
+
+ tailId := uint32(2) // First file is 2
+ itemOffset := uint32(1000000) // We have removed 1M items
+ zeroIndex := indexEntry{
+ offset: itemOffset,
+ filenum: tailId,
+ }
+ buf := zeroIndex.marshallBinary()
+ // Overwrite index zero
+ copy(indexBuf, buf)
+ indexFile.WriteAt(indexBuf, 0)
+ indexFile.Close()
+ }
+ checkPresent(1000000)
}
// TODO (?)
diff --git a/core/state/dump.go b/core/state/dump.go
index 051476c8a8..65c509a1da 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -27,6 +27,14 @@ import (
"github.com/celo-org/celo-blockchain/trie"
)
+// DumpCollector interface which the state trie calls during iteration
+type DumpCollector interface {
+ // OnRoot is called with the state root
+ OnRoot(common.Hash)
+ // OnAccount is called once for each account in the trie
+ OnAccount(common.Address, DumpAccount)
+}
+
// DumpAccount represents an account in the state.
type DumpAccount struct {
Balance string `json:"balance"`
@@ -46,9 +54,14 @@ type Dump struct {
Accounts map[common.Address]DumpAccount `json:"accounts"`
}
-// iterativeDump is a 'collector'-implementation which dump output line-by-line iteratively.
-type iterativeDump struct {
- *json.Encoder
+// OnRoot implements DumpCollector interface
+func (d *Dump) OnRoot(root common.Hash) {
+ d.Root = fmt.Sprintf("%x", root)
+}
+
+// OnAccount implements DumpCollector interface
+func (d *Dump) OnAccount(addr common.Address, account DumpAccount) {
+ d.Accounts[addr] = account
}
// IteratorDump is an implementation for iterating over data.
@@ -58,28 +71,23 @@ type IteratorDump struct {
Next []byte `json:"next,omitempty"` // nil if no more accounts
}
-// Collector interface which the state trie calls during iteration
-type collector interface {
- onRoot(common.Hash)
- onAccount(common.Address, DumpAccount)
-}
-
-func (d *Dump) onRoot(root common.Hash) {
+// OnRoot implements DumpCollector interface
+func (d *IteratorDump) OnRoot(root common.Hash) {
d.Root = fmt.Sprintf("%x", root)
}
-func (d *Dump) onAccount(addr common.Address, account DumpAccount) {
+// OnAccount implements DumpCollector interface
+func (d *IteratorDump) OnAccount(addr common.Address, account DumpAccount) {
d.Accounts[addr] = account
}
-func (d *IteratorDump) onRoot(root common.Hash) {
- d.Root = fmt.Sprintf("%x", root)
-}
-func (d *IteratorDump) onAccount(addr common.Address, account DumpAccount) {
- d.Accounts[addr] = account
+// iterativeDump is a DumpCollector-implementation which dumps output line-by-line iteratively.
+type iterativeDump struct {
+ *json.Encoder
}
-func (d iterativeDump) onAccount(addr common.Address, account DumpAccount) {
+// OnAccount implements DumpCollector interface
+func (d iterativeDump) OnAccount(addr common.Address, account DumpAccount) {
dumpAccount := &DumpAccount{
Balance: account.Balance,
Nonce: account.Nonce,
@@ -96,15 +104,16 @@ func (d iterativeDump) onAccount(addr common.Address, account DumpAccount) {
d.Encode(dumpAccount)
}
-func (d iterativeDump) onRoot(root common.Hash) {
+// OnRoot implements DumpCollector interface
+func (d iterativeDump) OnRoot(root common.Hash) {
d.Encode(struct {
Root common.Hash `json:"root"`
}{root})
}
-func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) (nextKey []byte) {
+func (s *StateDB) DumpToCollector(c DumpCollector, excludeCode, excludeStorage, excludeMissingPreimages bool, start []byte, maxResults int) (nextKey []byte) {
missingPreimages := 0
- c.onRoot(s.trie.Hash())
+ c.OnRoot(s.trie.Hash())
var count int
it := trie.NewIterator(s.trie.NodeIterator(start))
@@ -145,7 +154,7 @@ func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingP
account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
}
}
- c.onAccount(addr, account)
+ c.OnAccount(addr, account)
count++
if maxResults > 0 && count >= maxResults {
if it.Next() {
@@ -166,7 +175,7 @@ func (s *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages b
dump := &Dump{
Accounts: make(map[common.Address]DumpAccount),
}
- s.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0)
+ s.DumpToCollector(dump, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0)
return *dump
}
@@ -175,14 +184,14 @@ func (s *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool
dump := s.RawDump(excludeCode, excludeStorage, excludeMissingPreimages)
json, err := json.MarshalIndent(dump, "", " ")
if err != nil {
- fmt.Println("dump err", err)
+ fmt.Println("Dump err", err)
}
return json
}
// IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
func (s *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) {
- s.dump(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0)
+ s.DumpToCollector(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages, nil, 0)
}
// IteratorDump dumps out a batch of accounts starts with the given start key
@@ -190,6 +199,6 @@ func (s *StateDB) IteratorDump(excludeCode, excludeStorage, excludeMissingPreima
iterator := &IteratorDump{
Accounts: make(map[common.Address]DumpAccount),
}
- iterator.Next = s.dump(iterator, excludeCode, excludeStorage, excludeMissingPreimages, start, maxResults)
+ iterator.Next = s.DumpToCollector(iterator, excludeCode, excludeStorage, excludeMissingPreimages, start, maxResults)
return *iterator
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index aae44abd53..02d28fa713 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -213,14 +213,14 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
if _, destructed := s.db.snapDestructs[s.addrHash]; destructed {
return common.Hash{}
}
- enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key[:]))
+ enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes()))
}
// If snapshot unavailable or reading from it failed, load from the database
if s.db.snap == nil || err != nil {
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageReads += time.Since(start) }(time.Now())
}
- if enc, err = s.getTrie(db).TryGet(key[:]); err != nil {
+ if enc, err = s.getTrie(db).TryGet(key.Bytes()); err != nil {
s.setError(err)
return common.Hash{}
}
diff --git a/core/state/state_test.go b/core/state/state_test.go
index f9a612a288..fb8a958fe3 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -56,7 +56,7 @@ func TestDump(t *testing.T) {
s.state.updateStateObject(obj2)
s.state.Commit(false)
- // check that dump contains the state objects that are in trie
+ // check that DumpToCollector contains the state objects that are in trie
got := string(s.state.Dump(false, false, true))
want := `{
"root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
@@ -83,7 +83,7 @@ func TestDump(t *testing.T) {
}
}`
if got != want {
- t.Errorf("dump mismatch:\ngot: %s\nwant: %s\n", got, want)
+ t.Errorf("DumpToCollector mismatch:\ngot: %s\nwant: %s\n", got, want)
}
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 40805d2e82..554cd146de 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -513,7 +513,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
defer func(start time.Time) { s.SnapshotAccountReads += time.Since(start) }(time.Now())
}
var acc *snapshot.Account
- if acc, err = s.snap.Account(crypto.Keccak256Hash(addr[:])); err == nil {
+ if acc, err = s.snap.Account(crypto.Keccak256Hash(addr.Bytes())); err == nil {
if acc == nil {
return nil
}
@@ -532,9 +532,9 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if metrics.EnabledExpensive {
defer func(start time.Time) { s.AccountReads += time.Since(start) }(time.Now())
}
- enc, err := s.trie.TryGet(addr[:])
+ enc, err := s.trie.TryGet(addr.Bytes())
if err != nil {
- s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr[:], err))
+ s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %v", addr.Bytes(), err))
return nil
}
if len(enc) == 0 {
diff --git a/core/tx_list.go b/core/tx_list.go
index addea4fddd..3763de6c32 100644
--- a/core/tx_list.go
+++ b/core/tx_list.go
@@ -348,7 +348,7 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
// This method uses the cached costcap and gascap to quickly decide if there's even
// a point in calculating all the costs or if the balance covers all. If the threshold
// is lower than the costgas cap, the caps will be reset to a new high after removing
-func (l *txList) Filter(nativeCostLimit *big.Int, feeLimits map[common.Address]*big.Int, blockCtx BlockContext, gasLimit uint64) (types.Transactions, types.Transactions) {
+func (l *txList) Filter(nativeCostLimit *big.Int, feeLimits map[common.Address]*big.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
// check if we can bail & lower caps & raise floors at the same time
canBail := true
@@ -503,7 +503,7 @@ func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h priceHeap) Less(i, j int) bool {
// Sort primarily by price, returning the cheaper one
- switch h[i].GasPrice().Cmp(h[j].GasPrice()) {
+ switch h[i].GasPriceCmp(h[j]) {
case -1:
return true
case 1:
diff --git a/core/tx_pool.go b/core/tx_pool.go
index fc5dfb16ac..50a0a1feef 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -892,16 +892,22 @@ func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
knownTxMeter.Mark(1)
continue
}
+ // Exclude transactions with invalid signatures as soon as
+ // possible and cache senders in transactions before
+ // obtaining lock
+ _, err := types.Sender(pool.signer, tx)
+ if err != nil {
+ errs[i] = ErrInvalidSender
+ invalidTxMeter.Mark(1)
+ continue
+ }
// Accumulate all unknown transactions for deeper processing
news = append(news, tx)
}
if len(news) == 0 {
return errs
}
- // Cache senders in transactions before obtaining lock (pool.signer is immutable)
- for _, tx := range news {
- types.Sender(pool.signer, tx)
- }
+
// Process all the new transaction and merge any errors into the original slice
pool.mu.Lock()
newErrs, dirtyAddrs := pool.addTxsLocked(news, local)
@@ -1304,8 +1310,8 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
for _, tx := range forwards {
hash := tx.Hash()
pool.all.Remove(hash)
- log.Trace("Removed old queued transaction", "hash", hash)
}
+ log.Trace("Removed old queued transactions", "count", len(forwards))
// Get balances in each currency
balances := make(map[common.Address]*big.Int)
allCurrencies := list.FeeCurrencies()
@@ -1314,12 +1320,12 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
balances[feeCurrency] = feeCurrencyBalance
}
// Drop all transactions that are too costly (low balance or out of gas)
- drops, _ := list.Filter(pool.currentState.GetBalance(addr), balances, pool.ctx().BlockContext, pool.currentMaxGas)
+ drops, _ := list.Filter(pool.currentState.GetBalance(addr), balances, pool.currentMaxGas)
for _, tx := range drops {
hash := tx.Hash()
pool.all.Remove(hash)
- log.Trace("Removed unpayable queued transaction", "hash", hash)
}
+ log.Trace("Removed unpayable queued transactions", "count", len(drops))
queuedNofundsMeter.Mark(int64(len(drops)))
// Gather all executable transactions and promote them
@@ -1327,10 +1333,10 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
for _, tx := range readies {
hash := tx.Hash()
if pool.promoteTx(addr, hash, tx) {
- log.Trace("Promoting queued transaction", "hash", hash)
promoted = append(promoted, tx)
}
}
+ log.Trace("Promoted queued transactions", "count", len(promoted))
queuedGauge.Dec(int64(len(readies)))
// Drop all transactions over the allowed limit
@@ -1514,7 +1520,7 @@ func (pool *TxPool) demoteUnexecutables() {
balances[feeCurrency] = feeCurrencyBalance
}
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
- drops, invalids := list.Filter(pool.currentState.GetBalance(addr), balances, pool.ctx().BlockContext, pool.currentMaxGas)
+ drops, invalids := list.Filter(pool.currentState.GetBalance(addr), balances, pool.currentMaxGas)
for _, tx := range drops {
hash := tx.Hash()
log.Trace("Removed unpayable pending transaction", "hash", hash)
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index c1ed5d95ee..f398e8cdb1 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -18,6 +18,7 @@ package core
import (
"crypto/ecdsa"
+ "errors"
"fmt"
"io/ioutil"
"math/big"
@@ -274,7 +275,7 @@ func TestInvalidTransactions(t *testing.T) {
balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(tx.Gas()), tx.GasPrice()))
pool.currentState.AddBalance(from, balance)
- if err := pool.AddRemote(tx); err != ErrIntrinsicGas {
+ if err := pool.AddRemote(tx); !errors.Is(err, ErrIntrinsicGas) {
t.Error("expected", ErrIntrinsicGas, "got", err)
}
diff --git a/core/types/block.go b/core/types/block.go
index 091be50aab..cb26320c94 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -22,11 +22,13 @@ import (
"io"
"math/big"
"reflect"
+ "sync"
"sync/atomic"
"time"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/common/hexutil"
+ "github.com/celo-org/celo-blockchain/crypto"
blscrypto "github.com/celo-org/celo-blockchain/crypto/bls"
"github.com/celo-org/celo-blockchain/rlp"
"golang.org/x/crypto/sha3"
@@ -97,10 +99,19 @@ func (h *Header) SanityCheck() error {
return nil
}
+// hasherPool holds LegacyKeccak hashers.
+var hasherPool = sync.Pool{
+ New: func() interface{} {
+ return sha3.NewLegacyKeccak256()
+ },
+}
+
func rlpHash(x interface{}) (h common.Hash) {
- hw := sha3.NewLegacyKeccak256()
- rlp.Encode(hw, x)
- hw.Sum(h[:0])
+ sha := hasherPool.Get().(crypto.KeccakState)
+ defer hasherPool.Put(sha)
+ sha.Reset()
+ rlp.Encode(sha, x)
+ sha.Read(h[:])
return h
}
diff --git a/core/types/block_test.go b/core/types/block_test.go
index 47dbb0e0a6..fef3b8dd1d 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -18,10 +18,14 @@ package types
import (
"bytes"
+ "math/big"
"reflect"
"testing"
"github.com/celo-org/celo-blockchain/common"
+ "github.com/celo-org/celo-blockchain/common/math"
+ "github.com/celo-org/celo-blockchain/crypto"
+ "github.com/celo-org/celo-blockchain/params"
"github.com/celo-org/celo-blockchain/rlp"
)
@@ -56,3 +60,54 @@ func TestBlockEncoding(t *testing.T) {
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc)
}
}
+
+var benchBuffer = bytes.NewBuffer(make([]byte, 0, 32000))
+
+func BenchmarkEncodeBlock(b *testing.B) {
+ block := makeBenchBlock()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ benchBuffer.Reset()
+ if err := rlp.Encode(benchBuffer, block); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+func makeBenchBlock() *Block {
+ var (
+ key, _ = crypto.GenerateKey()
+ txs = make([]*Transaction, 70)
+ receipts = make([]*Receipt, len(txs))
+ signer = NewEIP155Signer(params.TestChainConfig.ChainID)
+ uncles = make([]*Header, 3)
+ )
+ header := &Header{
+ Number: math.BigPow(2, 9),
+ GasUsed: 1476322,
+ Time: 9876543,
+ Extra: []byte("coolest block on chain"),
+ }
+ for i := range txs {
+ amount := math.BigPow(2, int64(i))
+ price := big.NewInt(300000)
+ data := make([]byte, 100)
+ tx := NewTransaction(uint64(i), common.Address{}, amount, 123457, price, nil, nil, nil, data)
+ signedTx, err := SignTx(tx, signer, key)
+ if err != nil {
+ panic(err)
+ }
+ txs[i] = signedTx
+ receipts[i] = NewReceipt(make([]byte, 32), false, tx.Gas())
+ }
+ for i := range uncles {
+ uncles[i] = &Header{
+ Number: math.BigPow(2, 9),
+ GasUsed: 1476322,
+ Time: 9876543,
+ Extra: []byte("benchmark uncle"),
+ }
+ }
+ return NewBlock(header, txs, receipts, nil)
+}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 846fbc1748..16b0b30cce 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -277,13 +277,20 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
return nil
}
-func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
-func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
-func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
+func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
+func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit }
+func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
+func (tx *Transaction) GasPriceCmp(other *Transaction) int {
+ return tx.data.Price.Cmp(other.data.Price)
+}
+func (tx *Transaction) GasPriceIntCmp(other *big.Int) int {
+ return tx.data.Price.Cmp(other)
+}
func (tx *Transaction) FeeCurrency() *common.Address { return tx.data.FeeCurrency }
func (tx *Transaction) GatewayFeeRecipient() *common.Address { return tx.data.GatewayFeeRecipient }
func (tx *Transaction) GatewayFee() *big.Int { return tx.data.GatewayFee }
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) }
+func (tx *Transaction) ValueU64() uint64 { return tx.data.Amount.Uint64() }
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
func (tx *Transaction) CheckNonce() bool { return true }
func (tx *Transaction) EthCompatible() bool { return tx.data.EthCompatible }
diff --git a/core/vm/common.go b/core/vm/common.go
index 4f9eca8733..4c49bd6a11 100644
--- a/core/vm/common.go
+++ b/core/vm/common.go
@@ -17,15 +17,14 @@
package vm
import (
- "math/big"
-
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/common/math"
+ "github.com/holiman/uint256"
)
// calcMemSize64 calculates the required memory size, and returns
// the size and whether the result overflowed uint64
-func calcMemSize64(off, l *big.Int) (uint64, bool) {
+func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
if !l.IsUint64() {
return 0, true
}
@@ -35,16 +34,16 @@ func calcMemSize64(off, l *big.Int) (uint64, bool) {
// calcMemSize64WithUint calculates the required memory size, and returns
// the size and whether the result overflowed uint64
// Identical to calcMemSize64, but length is a uint64
-func calcMemSize64WithUint(off *big.Int, length64 uint64) (uint64, bool) {
+func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) {
// if length is zero, memsize is always zero, regardless of offset
if length64 == 0 {
return 0, false
}
// Check that offset doesn't overflow
- if !off.IsUint64() {
+ offset64, overflow := off.Uint64WithOverflow()
+ if overflow {
return 0, true
}
- offset64 := off.Uint64()
val := offset64 + length64
// if value < either of it's parts, then it overflowed
return val, val < offset64
@@ -64,22 +63,6 @@ func getData(data []byte, start uint64, size uint64) []byte {
return common.RightPadBytes(data[start:end], int(size))
}
-// getDataBig returns a slice from the data based on the start and size and pads
-// up to size with zero's. This function is overflow safe.
-func getDataBig(data []byte, start *big.Int, size *big.Int) []byte {
- dlen := big.NewInt(int64(len(data)))
-
- s := math.BigMin(start, dlen)
- e := math.BigMin(new(big.Int).Add(s, size), dlen)
- return common.RightPadBytes(data[s.Uint64():e.Uint64()], int(size.Uint64()))
-}
-
-// bigUint64 returns the integer casted to a uint64 and returns whether it
-// overflowed in the process.
-func bigUint64(v *big.Int) (uint64, bool) {
- return v.Uint64(), !v.IsUint64()
-}
-
// toWordSize returns the ceiled word size required for memory expansion.
func toWordSize(size uint64) uint64 {
if size > math.MaxUint64-31 {
diff --git a/core/vm/contract.go b/core/vm/contract.go
index b0eab8b2e5..91a0573a5f 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -21,6 +21,7 @@ import (
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/log"
+ "github.com/holiman/uint256"
)
// ContractRef is a reference to the contract's backing object
@@ -82,11 +83,11 @@ func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uin
return c
}
-func (c *Contract) validJumpdest(dest *big.Int) bool {
- udest := dest.Uint64()
- // PC cannot go beyond len(code) and certainly can't be bigger than 63 bits.
+func (c *Contract) validJumpdest(dest *uint256.Int) bool {
+ udest, overflow := dest.Uint64WithOverflow()
+ // PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
// Don't bother checking for JUMPDEST in that case.
- if dest.BitLen() >= 63 || udest >= uint64(len(c.Code)) {
+ if overflow || udest >= uint64(len(c.Code)) {
return false
}
// Only JUMPDESTs allowed for destinations
diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go
index cf2401fc2d..c8182f4754 100644
--- a/core/vm/contracts_test.go
+++ b/core/vm/contracts_test.go
@@ -25,6 +25,7 @@ import (
"math/big"
"reflect"
"testing"
+ "time"
blscrypto "github.com/celo-org/celo-blockchain/crypto/bls"
@@ -999,6 +1000,8 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
)
bench.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(bench *testing.B) {
+ bench.ReportAllocs()
+ start := time.Now().Nanosecond()
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
contract.Gas = reqGas
@@ -1006,6 +1009,13 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
res, err = RunPrecompiledContract(p, data, contract, mockEVM)
}
bench.StopTimer()
+ elapsed := float64(time.Now().Nanosecond() - start)
+ if elapsed < 1 {
+ elapsed = 1
+ }
+ gasUsed := reqGas * uint64(bench.N)
+ bench.ReportMetric(float64(reqGas), "gas/op")
+ bench.ReportMetric(float64(gasUsed*1000)/elapsed, "mgas/s")
//Check if it is correct
if err != nil {
bench.Error(err)
@@ -1393,3 +1403,42 @@ func TestPrecompiledBLS12381G2MultiExpFail(t *testing.T) { testJSONFail("fail-bl
func TestPrecompiledBLS12381PairingFail(t *testing.T) { testJSONFail("fail-blsPairing", "ec", t) }
func TestPrecompiledBLS12381MapG1Fail(t *testing.T) { testJSONFail("fail-blsMapG1", "eb", t) }
func TestPrecompiledBLS12381MapG2Fail(t *testing.T) { testJSONFail("fail-blsMapG2", "ea", t) }
+
+// BenchmarkPrecompiledBLS12381G1MultiExpWorstCase benchmarks the worst case we could find that still fits a gaslimit of 10MGas.
+func BenchmarkPrecompiledBLS12381G1MultiExpWorstCase(b *testing.B) {
+ task := "0000000000000000000000000000000008d8c4a16fb9d8800cce987c0eadbb6b3b005c213d44ecb5adeed713bae79d606041406df26169c35df63cf972c94be1" +
+ "0000000000000000000000000000000011bc8afe71676e6730702a46ef817060249cd06cd82e6981085012ff6d013aa4470ba3a2c71e13ef653e1e223d1ccfe9" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ input := task
+ for i := 0; i < 4787; i++ {
+ input = input + task
+ }
+ testcase := precompiledTest{
+ input: input,
+ expected: "0000000000000000000000000000000005a6310ea6f2a598023ae48819afc292b4dfcb40aabad24a0c2cb6c19769465691859eeb2a764342a810c5038d700f18000000000000000000000000000000001268ac944437d15923dc0aec00daa9250252e43e4b35ec7a19d01f0d6cd27f6e139d80dae16ba1c79cc7f57055a93ff5",
+ name: "WorstCaseG1",
+ noBenchmark: false,
+ }
+ benchmarkPrecompiled("0c", testcase, b)
+}
+
+// BenchmarkPrecompiledBLS12381G2MultiExpWorstCase benchmarks the worst case we could find that still fits a gaslimit of 10MGas.
+func BenchmarkPrecompiledBLS12381G2MultiExpWorstCase(b *testing.B) {
+ task := "000000000000000000000000000000000d4f09acd5f362e0a516d4c13c5e2f504d9bd49fdfb6d8b7a7ab35a02c391c8112b03270d5d9eefe9b659dd27601d18f" +
+ "000000000000000000000000000000000fd489cb75945f3b5ebb1c0e326d59602934c8f78fe9294a8877e7aeb95de5addde0cb7ab53674df8b2cfbb036b30b99" +
+ "00000000000000000000000000000000055dbc4eca768714e098bbe9c71cf54b40f51c26e95808ee79225a87fb6fa1415178db47f02d856fea56a752d185f86b" +
+ "000000000000000000000000000000001239b7640f416eb6e921fe47f7501d504fadc190d9cf4e89ae2b717276739a2f4ee9f637c35e23c480df029fd8d247c7" +
+ "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"
+ input := task
+ for i := 0; i < 1040; i++ {
+ input = input + task
+ }
+
+ testcase := precompiledTest{
+ input: input,
+ expected: "0000000000000000000000000000000018f5ea0c8b086095cfe23f6bb1d90d45de929292006dba8cdedd6d3203af3c6bbfd592e93ecb2b2c81004961fdcbb46c00000000000000000000000000000000076873199175664f1b6493a43c02234f49dc66f077d3007823e0343ad92e30bd7dc209013435ca9f197aca44d88e9dac000000000000000000000000000000000e6f07f4b23b511eac1e2682a0fc224c15d80e122a3e222d00a41fab15eba645a700b9ae84f331ae4ed873678e2e6c9b000000000000000000000000000000000bcb4849e460612aaed79617255fd30c03f51cf03d2ed4163ca810c13e1954b1e8663157b957a601829bb272a4e6c7b8",
+ name: "WorstCaseG2",
+ noBenchmark: false,
+ }
+ benchmarkPrecompiled("0f", testcase, b)
+}
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 735fe1a025..d501e00d1a 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -18,28 +18,44 @@ package vm
import (
"fmt"
+ "sort"
+
// "github.com/celo-org/celo-blockchain/params"
+ "github.com/holiman/uint256"
)
+var activators = map[int]func(*JumpTable){
+ 2200: enable2200,
+ 1884: enable1884,
+ 1344: enable1344,
+ 2315: enable2315,
+}
+
// EnableEIP enables the given EIP on the config.
// This operation writes in-place, and callers need to ensure that the globally
// defined jump tables are not polluted.
func EnableEIP(eipNum int, jt *JumpTable) error {
- switch eipNum {
- case 2200:
- enable2200(jt)
- case 1884:
- enable1884(jt)
- case 1344:
- enable1344(jt)
- case 2315:
- enable2315(jt)
- default:
+ enablerFn, ok := activators[eipNum]
+ if !ok {
return fmt.Errorf("undefined eip %d", eipNum)
}
+ enablerFn(jt)
return nil
}
+func ValidEip(eipNum int) bool {
+ _, ok := activators[eipNum]
+ return ok
+}
+func ActivateableEips() []string {
+ var nums []string
+ for k := range activators {
+ nums = append(nums, fmt.Sprintf("%d", k))
+ }
+ sort.Strings(nums)
+ return nums
+}
+
// enable1884 applies EIP-1884 to the given jump table:
// - Increase cost of BALANCE to 700
// - Increase cost of EXTCODEHASH to 700
@@ -64,7 +80,7 @@ func enable1884(jt *JumpTable) {
}
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- balance := interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
+ balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
callContext.stack.push(balance)
return nil, nil
}
@@ -84,7 +100,7 @@ func enable1344(jt *JumpTable) {
// opChainID implements CHAINID opcode
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- chainId := interpreter.intPool.get().Set(interpreter.evm.chainConfig.ChainID)
+ chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
callContext.stack.push(chainId)
return nil, nil
}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 33b576e6c7..636081d918 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -397,7 +397,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
// but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios
- evm.StateDB.AddBalance(addr, bigZero)
+ evm.StateDB.AddBalance(addr, big.NewInt(0))
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
diff --git a/core/vm/gas.go b/core/vm/gas.go
index bda326cdc7..5cf1d852d2 100644
--- a/core/vm/gas.go
+++ b/core/vm/gas.go
@@ -17,7 +17,7 @@
package vm
import (
- "math/big"
+ "github.com/holiman/uint256"
)
// Gas costs
@@ -34,7 +34,7 @@ const (
//
// The cost of gas was changed during the homestead price change HF.
// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
-func callGas(isEip150 bool, availableGas, base uint64, callCost *big.Int) (uint64, error) {
+func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) {
if isEip150 {
availableGas = availableGas - base
gas := availableGas - availableGas/64
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index f2695dc165..d9ef7efed5 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -70,7 +70,7 @@ func memoryCopierGas(stackpos int) gasFunc {
return 0, err
}
// And gas for copying data, charged per word at param.CopyGas
- words, overflow := bigUint64(stack.Back(stackpos))
+ words, overflow := stack.Back(stackpos).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@@ -96,7 +96,7 @@ var (
func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
y, x = stack.Back(1), stack.Back(0)
- current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
+ current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
)
// The legacy gas metering only takes into consideration the current state
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
@@ -131,11 +131,11 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
- value := common.BigToHash(y)
+ value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
- original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
+ original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
@@ -183,14 +183,14 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
// Gas sentry honoured, do the actual gas calculation based on the stored value
var (
y, x = stack.Back(1), stack.Back(0)
- current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
+ current = evm.StateDB.GetState(contract.Address(), common.Hash(x.Bytes32()))
)
- value := common.BigToHash(y)
+ value := common.Hash(y.Bytes32())
if current == value { // noop (1)
return params.SstoreNoopGasEIP2200, nil
}
- original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
+ original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.SstoreInitGasEIP2200, nil
@@ -219,7 +219,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
func makeGasLog(n uint64) gasFunc {
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
- requestedSize, overflow := bigUint64(stack.Back(1))
+ requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@@ -252,7 +252,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
if err != nil {
return 0, err
}
- wordGas, overflow := bigUint64(stack.Back(1))
+ wordGas, overflow := stack.Back(1).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@@ -286,7 +286,7 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
if err != nil {
return 0, err
}
- wordGas, overflow := bigUint64(stack.Back(2))
+ wordGas, overflow := stack.Back(2).Uint64WithOverflow()
if overflow {
return 0, ErrGasUintOverflow
}
@@ -328,8 +328,8 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
gas uint64
- transfersValue = stack.Back(2).Sign() != 0
- address = common.BigToAddress(stack.Back(1))
+ transfersValue = !stack.Back(2).IsZero()
+ address = common.Address(stack.Back(1).Bytes20())
)
if evm.chainRules.IsEIP158 {
if transfersValue && evm.StateDB.Empty(address) {
@@ -422,7 +422,7 @@ func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
// EIP150 homestead gas reprice fork:
if evm.chainRules.IsEIP150 {
gas = params.SelfdestructGasEIP150
- var address = common.BigToAddress(stack.Back(0))
+ var address = common.Address(stack.Back(0).Bytes20())
if evm.chainRules.IsEIP158 {
// if empty and transfers value
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index b409a7270d..8f1fd6af27 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -17,302 +17,170 @@
package vm
import (
- "math/big"
-
"github.com/celo-org/celo-blockchain/common"
- "github.com/celo-org/celo-blockchain/common/math"
"github.com/celo-org/celo-blockchain/core/types"
"github.com/celo-org/celo-blockchain/params"
+ "github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
-var (
- bigZero = new(big.Int)
- tt255 = math.BigPow(2, 255)
-)
-
func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- math.U256(y.Add(x, y))
-
- interpreter.intPool.putOne(x)
+ y.Add(&x, y)
return nil, nil
}
func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- math.U256(y.Sub(x, y))
-
- interpreter.intPool.putOne(x)
+ y.Sub(&x, y)
return nil, nil
}
func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
- callContext.stack.push(math.U256(x.Mul(x, y)))
-
- interpreter.intPool.putOne(y)
-
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Mul(&x, y)
return nil, nil
}
func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- if y.Sign() != 0 {
- math.U256(y.Div(x, y))
- } else {
- y.SetUint64(0)
- }
- interpreter.intPool.putOne(x)
+ y.Div(&x, y)
return nil, nil
}
func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop())
- res := interpreter.intPool.getZero()
-
- if y.Sign() == 0 || x.Sign() == 0 {
- callContext.stack.push(res)
- } else {
- if x.Sign() != y.Sign() {
- res.Div(x.Abs(x), y.Abs(y))
- res.Neg(res)
- } else {
- res.Div(x.Abs(x), y.Abs(y))
- }
- callContext.stack.push(math.U256(res))
- }
- interpreter.intPool.put(x, y)
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.SDiv(&x, y)
return nil, nil
}
func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
- if y.Sign() == 0 {
- callContext.stack.push(x.SetUint64(0))
- } else {
- callContext.stack.push(math.U256(x.Mod(x, y)))
- }
- interpreter.intPool.putOne(y)
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.Mod(&x, y)
return nil, nil
}
func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := math.S256(callContext.stack.pop()), math.S256(callContext.stack.pop())
- res := interpreter.intPool.getZero()
-
- if y.Sign() == 0 {
- callContext.stack.push(res)
- } else {
- if x.Sign() < 0 {
- res.Mod(x.Abs(x), y.Abs(y))
- res.Neg(res)
- } else {
- res.Mod(x.Abs(x), y.Abs(y))
- }
- callContext.stack.push(math.U256(res))
- }
- interpreter.intPool.put(x, y)
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.SMod(&x, y)
return nil, nil
}
func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- base, exponent := callContext.stack.pop(), callContext.stack.pop()
- // some shortcuts
- cmpToOne := exponent.Cmp(big1)
- if cmpToOne < 0 { // Exponent is zero
- // x ^ 0 == 1
- callContext.stack.push(base.SetUint64(1))
- } else if base.Sign() == 0 {
- // 0 ^ y, if y != 0, == 0
- callContext.stack.push(base.SetUint64(0))
- } else if cmpToOne == 0 { // Exponent is one
- // x ^ 1 == x
- callContext.stack.push(base)
- } else {
- callContext.stack.push(math.Exp(base, exponent))
- interpreter.intPool.putOne(base)
- }
- interpreter.intPool.putOne(exponent)
+ base, exponent := callContext.stack.pop(), callContext.stack.peek()
+ exponent.Exp(&base, exponent)
return nil, nil
}
func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- back := callContext.stack.pop()
- if back.Cmp(big.NewInt(31)) < 0 {
- bit := uint(back.Uint64()*8 + 7)
- num := callContext.stack.pop()
- mask := back.Lsh(common.Big1, bit)
- mask.Sub(mask, common.Big1)
- if num.Bit(int(bit)) > 0 {
- num.Or(num, mask.Not(mask))
- } else {
- num.And(num, mask)
- }
-
- callContext.stack.push(math.U256(num))
- }
-
- interpreter.intPool.putOne(back)
+ back, num := callContext.stack.pop(), callContext.stack.peek()
+ num.ExtendSign(num, &back)
return nil, nil
}
func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x := callContext.stack.peek()
- math.U256(x.Not(x))
+ x.Not(x)
return nil, nil
}
func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- if x.Cmp(y) < 0 {
- y.SetUint64(1)
+ if x.Lt(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- if x.Cmp(y) > 0 {
- y.SetUint64(1)
+ if x.Gt(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
-
- xSign := x.Cmp(tt255)
- ySign := y.Cmp(tt255)
-
- switch {
- case xSign >= 0 && ySign < 0:
- y.SetUint64(1)
-
- case xSign < 0 && ySign >= 0:
- y.SetUint64(0)
-
- default:
- if x.Cmp(y) < 0 {
- y.SetUint64(1)
- } else {
- y.SetUint64(0)
- }
+ if x.Slt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
-
- xSign := x.Cmp(tt255)
- ySign := y.Cmp(tt255)
-
- switch {
- case xSign >= 0 && ySign < 0:
- y.SetUint64(0)
-
- case xSign < 0 && ySign >= 0:
- y.SetUint64(1)
-
- default:
- if x.Cmp(y) > 0 {
- y.SetUint64(1)
- } else {
- y.SetUint64(0)
- }
+ if x.Sgt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- if x.Cmp(y) == 0 {
- y.SetUint64(1)
+ if x.Eq(y) {
+ y.SetOne()
} else {
- y.SetUint64(0)
+ y.Clear()
}
- interpreter.intPool.putOne(x)
return nil, nil
}
func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x := callContext.stack.peek()
- if x.Sign() > 0 {
- x.SetUint64(0)
+ if x.IsZero() {
+ x.SetOne()
} else {
- x.SetUint64(1)
+ x.Clear()
}
return nil, nil
}
func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y := callContext.stack.pop(), callContext.stack.pop()
- callContext.stack.push(x.And(x, y))
-
- interpreter.intPool.putOne(y)
+ x, y := callContext.stack.pop(), callContext.stack.peek()
+ y.And(&x, y)
return nil, nil
}
func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- y.Or(x, y)
-
- interpreter.intPool.putOne(x)
+ y.Or(&x, y)
return nil, nil
}
func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
x, y := callContext.stack.pop(), callContext.stack.peek()
- y.Xor(x, y)
-
- interpreter.intPool.putOne(x)
+ y.Xor(&x, y)
return nil, nil
}
func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
th, val := callContext.stack.pop(), callContext.stack.peek()
- if th.Cmp(common.Big32) < 0 {
- b := math.Byte(val, 32, int(th.Int64()))
- val.SetUint64(uint64(b))
- } else {
- val.SetUint64(0)
- }
- interpreter.intPool.putOne(th)
+ val.Byte(&th)
return nil, nil
}
func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- if z.Cmp(bigZero) > 0 {
- x.Add(x, y)
- x.Mod(x, z)
- callContext.stack.push(math.U256(x))
+ x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+ if z.IsZero() {
+ z.Clear()
} else {
- callContext.stack.push(x.SetUint64(0))
+ z.AddMod(&x, &y, z)
}
- interpreter.intPool.put(y, z)
return nil, nil
}
func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- if z.Cmp(bigZero) > 0 {
- x.Mul(x, y)
- x.Mod(x, z)
- callContext.stack.push(math.U256(x))
- } else {
- callContext.stack.push(x.SetUint64(0))
- }
- interpreter.intPool.put(y, z)
+ x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+ z.MulMod(&x, &y, z)
return nil, nil
}
@@ -321,16 +189,12 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := math.U256(callContext.stack.pop()), math.U256(callContext.stack.peek())
- defer interpreter.intPool.putOne(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
- value.SetUint64(0)
- return nil, nil
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.LtUint64(256) {
+ value.Lsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
}
- n := uint(shift.Uint64())
- value.SetBytes(math.U256(new(big.Int).Lsh(value, n)).Bytes())
-
return nil, nil
}
@@ -339,16 +203,12 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
- shift, value := math.U256(callContext.stack.pop()), math.U256(callContext.stack.peek())
- defer interpreter.intPool.putOne(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
- value.SetUint64(0)
- return nil, nil
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.LtUint64(256) {
+ value.Rsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
}
- n := uint(shift.Uint64())
- math.U256(value.Rsh(value, n))
-
return nil, nil
}
@@ -356,29 +216,24 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- // Note, S256 returns (potentially) a new bigint, so we're popping, not peeking this one
- shift, value := math.U256(callContext.stack.pop()), math.S256(callContext.stack.pop())
- defer interpreter.intPool.putOne(shift) // First operand back into the pool
-
- if shift.Cmp(common.Big256) >= 0 {
+ shift, value := callContext.stack.pop(), callContext.stack.peek()
+ if shift.GtUint64(256) {
if value.Sign() >= 0 {
- value.SetUint64(0)
+ value.Clear()
} else {
- value.SetInt64(-1)
+ // Max negative shift: all bits set
+ value.SetAllOne()
}
- callContext.stack.push(math.U256(value))
return nil, nil
}
n := uint(shift.Uint64())
- value.Rsh(value, n)
- callContext.stack.push(math.U256(value))
-
+ value.SRsh(value, n)
return nil, nil
}
func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- offset, size := callContext.stack.pop(), callContext.stack.pop()
- data := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+ offset, size := callContext.stack.pop(), callContext.stack.peek()
+ data := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
if interpreter.hasher == nil {
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
@@ -392,45 +247,50 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
if evm.vmConfig.EnablePreimageRecording {
evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
}
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.hasherBuf[:]))
- interpreter.intPool.put(offset, size)
+ size.SetBytes(interpreter.hasherBuf[:])
return nil, nil
}
-
func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Address().Bytes()))
+ callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
return nil, nil
}
func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek()
- slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot)))
+ address := common.Address(slot.Bytes20())
+ slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
return nil, nil
}
func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Origin.Bytes()))
+ callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
return nil, nil
}
-
func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(callContext.contract.Caller().Bytes()))
+ callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
return nil, nil
}
func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().Set(callContext.contract.value))
+ v, _ := uint256.FromBig(callContext.contract.value)
+ callContext.stack.push(v)
return nil, nil
}
func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(getDataBig(callContext.contract.Input, callContext.stack.pop(), big32)))
+ x := callContext.stack.peek()
+ if offset, overflow := x.Uint64WithOverflow(); !overflow {
+ data := getData(callContext.contract.Input, offset, 32)
+ x.SetBytes(data)
+ } else {
+ x.Clear()
+ }
return nil, nil
}
func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Input))))
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
return nil, nil
}
@@ -440,14 +300,20 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
dataOffset = callContext.stack.pop()
length = callContext.stack.pop()
)
- callContext.memory.Set(memOffset.Uint64(), length.Uint64(), getDataBig(callContext.contract.Input, dataOffset, length))
+ dataOffset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ dataOffset64 = 0xffffffffffffffff
+ }
+ // These values are checked for overflow during gas cost calculation
+ memOffset64 := memOffset.Uint64()
+ length64 := length.Uint64()
+ callContext.memory.Set(memOffset64, length64, getData(callContext.contract.Input, dataOffset64, length64))
- interpreter.intPool.put(memOffset, dataOffset, length)
return nil, nil
}
func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(uint64(len(interpreter.returnData))))
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
return nil, nil
}
@@ -456,30 +322,33 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
memOffset = callContext.stack.pop()
dataOffset = callContext.stack.pop()
length = callContext.stack.pop()
-
- end = interpreter.intPool.get().Add(dataOffset, length)
)
- defer interpreter.intPool.put(memOffset, dataOffset, length, end)
- if !end.IsUint64() || uint64(len(interpreter.returnData)) < end.Uint64() {
+ offset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
return nil, ErrReturnDataOutOfBounds
}
- callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[dataOffset.Uint64():end.Uint64()])
-
+ // we can reuse dataOffset now (aliasing it for clarity)
+ var end = dataOffset
+ end.Add(&dataOffset, &length)
+ end64, overflow := end.Uint64WithOverflow()
+ if overflow || uint64(len(interpreter.returnData)) < end64 {
+ return nil, ErrReturnDataOutOfBounds
+ }
+ callContext.memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
return nil, nil
}
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek()
- slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot))))
-
+ slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.Address(slot.Bytes20()))))
return nil, nil
}
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- l := interpreter.intPool.get().SetInt64(int64(len(callContext.contract.Code)))
+ l := new(uint256.Int)
+ l.SetUint64(uint64(len(callContext.contract.Code)))
callContext.stack.push(l)
-
return nil, nil
}
@@ -489,24 +358,32 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
codeOffset = callContext.stack.pop()
length = callContext.stack.pop()
)
- codeCopy := getDataBig(callContext.contract.Code, codeOffset, length)
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ codeCopy := getData(callContext.contract.Code, uint64CodeOffset, length.Uint64())
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
- interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
- addr = common.BigToAddress(callContext.stack.pop())
- memOffset = callContext.stack.pop()
- codeOffset = callContext.stack.pop()
- length = callContext.stack.pop()
+ stack = callContext.stack
+ a = stack.pop()
+ memOffset = stack.pop()
+ codeOffset = stack.pop()
+ length = stack.pop()
)
- codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length)
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ addr := common.Address(a.Bytes20())
+ codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
callContext.memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
- interpreter.intPool.put(memOffset, codeOffset, length)
return nil, nil
}
@@ -538,9 +415,9 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
// this account should be regarded as a non-existent account and zero should be returned.
func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
slot := callContext.stack.peek()
- address := common.BigToAddress(slot)
+ address := common.Address(slot.Bytes20())
if interpreter.evm.StateDB.Empty(address) {
- slot.SetUint64(0)
+ slot.Clear()
} else {
slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
}
@@ -548,46 +425,58 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
}
func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().Set(interpreter.evm.GasPrice))
+ v, _ := uint256.FromBig(interpreter.evm.GasPrice)
+ callContext.stack.push(v)
return nil, nil
}
func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- num := callContext.stack.pop()
-
- n := interpreter.intPool.get().Sub(interpreter.evm.BlockNumber, common.Big257)
- if num.Cmp(n) > 0 && num.Cmp(interpreter.evm.BlockNumber) < 0 {
- callContext.stack.push(interpreter.evm.GetHash(num.Uint64()).Big())
+ num := callContext.stack.peek()
+ num64, overflow := num.Uint64WithOverflow()
+ if overflow {
+ num.Clear()
+ return nil, nil
+ }
+ var upper, lower uint64
+ upper = interpreter.evm.BlockNumber.Uint64()
+ if upper < 257 {
+ lower = 0
} else {
- callContext.stack.push(interpreter.intPool.getZero())
+ lower = upper - 256
+ }
+ if num64 >= lower && num64 < upper {
+ num.SetBytes(interpreter.evm.GetHash(num64).Bytes())
+ } else {
+ num.Clear()
}
- interpreter.intPool.put(num, n)
return nil, nil
}
func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetBytes(interpreter.evm.Coinbase.Bytes()))
+ callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
return nil, nil
}
func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.Time)))
+ v, _ := uint256.FromBig(interpreter.evm.Time)
+ callContext.stack.push(v)
return nil, nil
}
func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(math.U256(interpreter.intPool.get().Set(interpreter.evm.BlockNumber)))
+ v, _ := uint256.FromBig(interpreter.evm.BlockNumber)
+ callContext.stack.push(v)
return nil, nil
}
func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- interpreter.intPool.putOne(callContext.stack.pop())
+ callContext.stack.pop()
return nil, nil
}
func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
v := callContext.stack.peek()
- offset := v.Int64()
+ offset := int64(v.Uint64())
v.SetBytes(callContext.memory.GetPtr(offset, 32))
return nil, nil
}
@@ -595,58 +484,51 @@ func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b
func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// pop value of the stack
mStart, val := callContext.stack.pop(), callContext.stack.pop()
- callContext.memory.Set32(mStart.Uint64(), val)
-
- interpreter.intPool.put(mStart, val)
+ callContext.memory.Set32(mStart.Uint64(), &val)
return nil, nil
}
func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- off, val := callContext.stack.pop().Int64(), callContext.stack.pop().Int64()
- callContext.memory.store[off] = byte(val & 0xff)
-
+ off, val := callContext.stack.pop(), callContext.stack.pop()
+ callContext.memory.store[off.Uint64()] = byte(val.Uint64())
return nil, nil
}
func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
loc := callContext.stack.peek()
- val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), common.BigToHash(loc))
+ hash := common.Hash(loc.Bytes32())
+ val := interpreter.evm.StateDB.GetState(callContext.contract.Address(), hash)
loc.SetBytes(val.Bytes())
return nil, nil
}
func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- loc := common.BigToHash(callContext.stack.pop())
+ loc := callContext.stack.pop()
val := callContext.stack.pop()
- interpreter.evm.StateDB.SetState(callContext.contract.Address(), loc, common.BigToHash(val))
-
- interpreter.intPool.putOne(val)
+ interpreter.evm.StateDB.SetState(callContext.contract.Address(),
+ common.Hash(loc.Bytes32()), common.Hash(val.Bytes32()))
return nil, nil
}
func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
pos := callContext.stack.pop()
- if !callContext.contract.validJumpdest(pos) {
+ if !callContext.contract.validJumpdest(&pos) {
return nil, ErrInvalidJump
}
*pc = pos.Uint64()
-
- interpreter.intPool.putOne(pos)
return nil, nil
}
func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
pos, cond := callContext.stack.pop(), callContext.stack.pop()
- if cond.Sign() != 0 {
- if !callContext.contract.validJumpdest(pos) {
+ if !cond.IsZero() {
+ if !callContext.contract.validJumpdest(&pos) {
return nil, ErrInvalidJump
}
*pc = pos.Uint64()
} else {
*pc++
}
-
- interpreter.intPool.put(pos, cond)
return nil, nil
}
@@ -672,7 +554,6 @@ func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
}
callContext.rstack.push(*pc)
*pc = posU64 + 1
- interpreter.intPool.put(pos)
return nil, nil
}
@@ -688,17 +569,17 @@ func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
}
func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(*pc))
+ callContext.stack.push(new(uint256.Int).SetUint64(*pc))
return nil, nil
}
func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetInt64(int64(callContext.memory.Len())))
+ callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
return nil, nil
}
func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.push(interpreter.intPool.get().SetUint64(callContext.contract.Gas))
+ callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas))
return nil, nil
}
@@ -706,28 +587,30 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
var (
value = callContext.stack.pop()
offset, size = callContext.stack.pop(), callContext.stack.pop()
- input = callContext.memory.GetCopy(offset.Int64(), size.Int64())
+ input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = callContext.contract.Gas
)
if interpreter.evm.chainRules.IsEIP150 {
gas -= gas / 64
}
+ // reuse size int for stackvalue
+ stackvalue := size
callContext.contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value)
+ res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig())
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas {
- callContext.stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
- callContext.stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ stackvalue.SetBytes(addr.Bytes())
}
+ callContext.stack.push(&stackvalue)
callContext.contract.Gas += returnGas
- interpreter.intPool.put(value, offset, size)
if suberr == ErrExecutionReverted {
return res, nil
@@ -740,22 +623,25 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
endowment = callContext.stack.pop()
offset, size = callContext.stack.pop(), callContext.stack.pop()
salt = callContext.stack.pop()
- input = callContext.memory.GetCopy(offset.Int64(), size.Int64())
+ input = callContext.memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = callContext.contract.Gas
)
// Apply EIP150
gas -= gas / 64
callContext.contract.UseGas(gas)
- res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas, endowment, salt)
+ // reuse size int for stackvalue
+ stackvalue := size
+ res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
+ endowment.ToBig(), salt.ToBig())
// Push item on the stack based on the returned error.
if suberr != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ stackvalue.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetBytes(addr.Bytes()))
+ stackvalue.SetBytes(addr.Bytes())
}
+ callContext.stack.push(&stackvalue)
callContext.contract.Gas += returnGas
- interpreter.intPool.put(endowment, offset, size, salt)
if suberr == ErrExecutionReverted {
return res, nil
@@ -764,126 +650,130 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
}
func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ // We can use this as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- toAddr := common.BigToAddress(addr)
- value = math.U256(value)
+ addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
// Get the arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ if !value.IsZero() {
gas += params.CallStipend
}
- ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value)
+ ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig())
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ stack := callContext.stack
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, value, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- toAddr := common.BigToAddress(addr)
- value = math.U256(value)
+ addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- if value.Sign() != 0 {
+ if !value.IsZero() {
gas += params.CallStipend
}
- ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value)
+ ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig())
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, value, inOffset, inSize, retOffset, retSize)
return ret, nil
}
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ stack := callContext.stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- toAddr := common.BigToAddress(addr)
+ addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
ret, returnGas, err := interpreter.evm.DelegateCall(callContext.contract, toAddr, args, gas)
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
- interpreter.intPool.putOne(callContext.stack.pop())
+ stack := callContext.stack
+ // We use it as a temporary value
+ temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
- addr, inOffset, inSize, retOffset, retSize := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
- toAddr := common.BigToAddress(addr)
+ addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
- args := callContext.memory.GetPtr(inOffset.Int64(), inSize.Int64())
+ args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
ret, returnGas, err := interpreter.evm.StaticCall(callContext.contract, toAddr, args, gas)
if err != nil {
- callContext.stack.push(interpreter.intPool.getZero())
+ temp.Clear()
} else {
- callContext.stack.push(interpreter.intPool.get().SetUint64(1))
+ temp.SetOne()
}
+ stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
callContext.memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
callContext.contract.Gas += returnGas
- interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
return ret, nil
}
func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop()
- ret := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+ ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
- interpreter.intPool.put(offset, size)
return ret, nil
}
func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
offset, size := callContext.stack.pop(), callContext.stack.pop()
- ret := callContext.memory.GetPtr(offset.Int64(), size.Int64())
+ ret := callContext.memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
- interpreter.intPool.put(offset, size)
return ret, nil
}
@@ -892,9 +782,9 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
}
func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
+ beneficiary := callContext.stack.pop()
balance := interpreter.evm.StateDB.GetBalance(callContext.contract.Address())
- interpreter.evm.StateDB.AddBalance(common.BigToAddress(callContext.stack.pop()), balance)
-
+ interpreter.evm.StateDB.AddBalance(common.Address(beneficiary.Bytes20()), balance)
interpreter.evm.StateDB.Suicide(callContext.contract.Address())
return nil, nil
}
@@ -905,12 +795,14 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
func makeLog(size int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
topics := make([]common.Hash, size)
- mStart, mSize := callContext.stack.pop(), callContext.stack.pop()
+ stack := callContext.stack
+ mStart, mSize := stack.pop(), stack.pop()
for i := 0; i < size; i++ {
- topics[i] = common.BigToHash(callContext.stack.pop())
+ addr := stack.pop()
+ topics[i] = common.Hash(addr.Bytes32())
}
- d := callContext.memory.GetCopy(mStart.Int64(), mSize.Int64())
+ d := callContext.memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
interpreter.evm.StateDB.AddLog(&types.Log{
Address: callContext.contract.Address(),
Topics: topics,
@@ -920,7 +812,6 @@ func makeLog(size int) executionFunc {
BlockNumber: interpreter.evm.BlockNumber.Uint64(),
})
- interpreter.intPool.put(mStart, mSize)
return nil, nil
}
}
@@ -929,13 +820,13 @@ func makeLog(size int) executionFunc {
func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
var (
codeLen = uint64(len(callContext.contract.Code))
- integer = interpreter.intPool.get()
+ integer = new(uint256.Int)
)
*pc += 1
if *pc < codeLen {
callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
} else {
- callContext.stack.push(integer.SetUint64(0))
+ callContext.stack.push(integer.Clear())
}
return nil, nil
}
@@ -955,8 +846,9 @@ func makePush(size uint64, pushByteSize int) executionFunc {
endMin = startMin + pushByteSize
}
- integer := interpreter.intPool.get()
- callContext.stack.push(integer.SetBytes(common.RightPadBytes(callContext.contract.Code[startMin:endMin], pushByteSize)))
+ integer := new(uint256.Int)
+ callContext.stack.push(integer.SetBytes(common.RightPadBytes(
+ callContext.contract.Code[startMin:endMin], pushByteSize)))
*pc += size
return nil, nil
@@ -966,7 +858,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
// make dup instruction function
func makeDup(size int64) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
- callContext.stack.dup(interpreter.intPool, int(size))
+ callContext.stack.dup(int(size))
return nil, nil
}
}
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index d433d3d9da..dff40fc4a5 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -21,12 +21,12 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
- "math/big"
"testing"
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/crypto"
"github.com/celo-org/celo-blockchain/params"
+ "github.com/holiman/uint256"
)
type TwoOperandTestcase struct {
@@ -98,42 +98,23 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
pc = uint64(0)
evmInterpreter = env.interpreter.(*EVMInterpreter)
)
- // Stuff a couple of nonzero bigints into pool, to ensure that ops do not rely on pooled integers to be zero
- evmInterpreter.intPool = poolOfIntPools.get()
- evmInterpreter.intPool.put(big.NewInt(-1337))
- evmInterpreter.intPool.put(big.NewInt(-1337))
- evmInterpreter.intPool.put(big.NewInt(-1337))
for i, test := range tests {
- x := new(big.Int).SetBytes(common.Hex2Bytes(test.X))
- y := new(big.Int).SetBytes(common.Hex2Bytes(test.Y))
- expected := new(big.Int).SetBytes(common.Hex2Bytes(test.Expected))
+ x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X))
+ y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y))
+ expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
stack.push(x)
stack.push(y)
opFn(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
+ if len(stack.data) != 1 {
+ t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
+ }
actual := stack.pop()
if actual.Cmp(expected) != 0 {
t.Errorf("Testcase %v %d, %v(%x, %x): expected %x, got %x", name, i, name, x, y, expected, actual)
}
- // Check pool usage
- // 1.pool is not allowed to contain anything on the stack
- // 2.pool is not allowed to contain the same pointers twice
- if evmInterpreter.intPool.pool.len() > 0 {
-
- poolvals := make(map[*big.Int]struct{})
- poolvals[actual] = struct{}{}
-
- for evmInterpreter.intPool.pool.len() > 0 {
- key := evmInterpreter.intPool.get()
- if _, exist := poolvals[key]; exist {
- t.Errorf("Testcase %v %d, pool contains double-entry", name, i)
- }
- poolvals[key] = struct{}{}
- }
- }
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func TestByteOp(t *testing.T) {
@@ -209,6 +190,44 @@ func TestSAR(t *testing.T) {
testTwoOperandOp(t, tests, opSAR, "sar")
}
+func TestAddMod(t *testing.T) {
+ var (
+ env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+ stack = newstack()
+ evmInterpreter = NewEVMInterpreter(env, &env.vmConfig)
+ pc = uint64(0)
+ )
+ tests := []struct {
+ x string
+ y string
+ z string
+ expected string
+ }{
+ {"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
+ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
+ },
+ }
+ // x + y = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
+ // in 256 bit repr, fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd
+
+ for i, test := range tests {
+ x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.x))
+ y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.y))
+ z := new(uint256.Int).SetBytes(common.Hex2Bytes(test.z))
+ expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.expected))
+ stack.push(z)
+ stack.push(y)
+ stack.push(x)
+ opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil, nil})
+ actual := stack.pop()
+ if actual.Cmp(expected) != 0 {
+ t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
+ }
+ }
+}
+
// getResult is a convenience function to generate the expected values
func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
var (
@@ -217,11 +236,10 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
pc = uint64(0)
interpreter = env.interpreter.(*EVMInterpreter)
)
- interpreter.intPool = poolOfIntPools.get()
result := make([]TwoOperandTestcase, len(args))
for i, param := range args {
- x := new(big.Int).SetBytes(common.Hex2Bytes(param.x))
- y := new(big.Int).SetBytes(common.Hex2Bytes(param.y))
+ x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
+ y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
stack.push(x)
stack.push(y)
opFn(&pc, interpreter, &callCtx{nil, stack, rstack, nil})
@@ -269,7 +287,6 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
)
env.interpreter = evmInterpreter
- evmInterpreter.intPool = poolOfIntPools.get()
// convert args
byteArgs := make([][]byte, len(args))
for i, arg := range args {
@@ -279,13 +296,13 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
for _, arg := range byteArgs {
- a := new(big.Int).SetBytes(arg)
+ a := new(uint256.Int)
+ a.SetBytes(arg)
stack.push(a)
}
op(&pc, evmInterpreter, &callCtx{nil, stack, rstack, nil})
stack.pop()
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpAdd64(b *testing.B) {
@@ -505,21 +522,19 @@ func TestOpMstore(t *testing.T) {
)
env.interpreter = evmInterpreter
- evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(64)
pc := uint64(0)
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
- stack.pushN(new(big.Int).SetBytes(common.Hex2Bytes(v)), big.NewInt(0))
+ stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int))
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
}
- stack.pushN(big.NewInt(0x1), big.NewInt(0))
+ stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int))
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
t.Fatalf("Mstore failed to overwrite previous value")
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpMstore(bench *testing.B) {
@@ -531,18 +546,16 @@ func BenchmarkOpMstore(bench *testing.B) {
)
env.interpreter = evmInterpreter
- evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(64)
pc := uint64(0)
- memStart := big.NewInt(0)
- value := big.NewInt(0x1337)
+ memStart := new(uint256.Int)
+ value := new(uint256.Int).SetUint64(0x1337)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- stack.pushN(value, memStart)
+ stack.pushN(*value, *memStart)
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func BenchmarkOpSHA3(bench *testing.B) {
@@ -553,17 +566,15 @@ func BenchmarkOpSHA3(bench *testing.B) {
evmInterpreter = NewEVMInterpreter(env, &env.vmConfig)
)
env.interpreter = evmInterpreter
- evmInterpreter.intPool = poolOfIntPools.get()
mem.Resize(32)
pc := uint64(0)
- start := big.NewInt(0)
+ start := uint256.NewInt()
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
- stack.pushN(big.NewInt(32), start)
+ stack.pushN(*uint256.NewInt().SetUint64(32), *start)
opSha3(&pc, evmInterpreter, &callCtx{mem, stack, rstack, nil})
}
- poolOfIntPools.put(evmInterpreter.intPool)
}
func TestCreate2Addreses(t *testing.T) {
@@ -637,6 +648,5 @@ func TestCreate2Addreses(t *testing.T) {
if !bytes.Equal(expected.Bytes(), address.Bytes()) {
t.Errorf("test %d: expected %s, got %s", i, expected.String(), address.String())
}
-
}
}
diff --git a/core/vm/int_pool_verifier.go b/core/vm/int_pool_verifier.go
deleted file mode 100644
index 82fbfed699..0000000000
--- a/core/vm/int_pool_verifier.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// +build VERIFY_EVM_INTEGER_POOL
-
-package vm
-
-import "fmt"
-
-const verifyPool = true
-
-func verifyIntegerPool(ip *intPool) {
- for i, item := range ip.pool.data {
- if item.Cmp(checkVal) != 0 {
- panic(fmt.Sprintf("%d'th item failed aggressive pool check. Value was modified", i))
- }
- }
-}
diff --git a/core/vm/int_pool_verifier_empty.go b/core/vm/int_pool_verifier_empty.go
deleted file mode 100644
index a5f1dc02b7..0000000000
--- a/core/vm/int_pool_verifier_empty.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-// +build !VERIFY_EVM_INTEGER_POOL
-
-package vm
-
-const verifyPool = false
-
-func verifyIntegerPool(ip *intPool) {}
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index cb4265f1d2..41bae0bad3 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -84,8 +84,6 @@ type EVMInterpreter struct {
evm *EVM
cfg *Config
- intPool *intPool
-
hasher keccakState // Keccak256 hasher instance shared across opcodes
hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
@@ -139,13 +137,6 @@ func NewEVMInterpreter(evm *EVM, cfg *Config) *EVMInterpreter {
// considered a revert-and-consume-all-gas operation except for
// ErrExecutionReverted which means revert-and-keep-gas-left.
func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
- if in.intPool == nil {
- in.intPool = poolOfIntPools.get()
- defer func() {
- poolOfIntPools.put(in.intPool)
- in.intPool = nil
- }()
- }
// Increment the call depth which is restricted to 1024
in.evm.depth++
@@ -191,9 +182,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
)
contract.Input = input
- // Reclaim the stack as an int pool when the execution stops
- defer func() { in.intPool.put(stack.data...) }()
-
if in.cfg.Debug {
defer func() {
if err != nil {
@@ -291,11 +279,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
// execute the operation
res, err = operation.execute(&pc, in, callContext)
- // verifyPool is a build flag. Pool verification makes sure the integrity
- // of the integer pool by comparing values to a default value.
- if verifyPool {
- verifyIntegerPool(in.intPool)
- }
// if the operation clears the return data (e.g. it has returning data)
// set the last return to the result of the operation.
if operation.returns {
diff --git a/core/vm/intpool.go b/core/vm/intpool.go
deleted file mode 100644
index eed074b073..0000000000
--- a/core/vm/intpool.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package vm
-
-import (
- "math/big"
- "sync"
-)
-
-var checkVal = big.NewInt(-42)
-
-const poolLimit = 256
-
-// intPool is a pool of big integers that
-// can be reused for all big.Int operations.
-type intPool struct {
- pool *Stack
-}
-
-func newIntPool() *intPool {
- return &intPool{pool: newstack()}
-}
-
-// get retrieves a big int from the pool, allocating one if the pool is empty.
-// Note, the returned int's value is arbitrary and will not be zeroed!
-func (p *intPool) get() *big.Int {
- if p.pool.len() > 0 {
- return p.pool.pop()
- }
- return new(big.Int)
-}
-
-// getZero retrieves a big int from the pool, setting it to zero or allocating
-// a new one if the pool is empty.
-func (p *intPool) getZero() *big.Int {
- if p.pool.len() > 0 {
- return p.pool.pop().SetUint64(0)
- }
- return new(big.Int)
-}
-
-// putOne returns an allocated big int to the pool to be later reused by get calls.
-// Note, the values as saved as is; neither put nor get zeroes the ints out!
-// As opposed to 'put' with variadic args, this method becomes inlined by the
-// go compiler
-func (p *intPool) putOne(i *big.Int) {
- if len(p.pool.data) > poolLimit {
- return
- }
- p.pool.push(i)
-}
-
-// put returns an allocated big int to the pool to be later reused by get calls.
-// Note, the values as saved as is; neither put nor get zeroes the ints out!
-func (p *intPool) put(is ...*big.Int) {
- if len(p.pool.data) > poolLimit {
- return
- }
- for _, i := range is {
- // verifyPool is a build flag. Pool verification makes sure the integrity
- // of the integer pool by comparing values to a default value.
- if verifyPool {
- i.Set(checkVal)
- }
- p.pool.push(i)
- }
-}
-
-// The intPool pool's default capacity
-const poolDefaultCap = 25
-
-// intPoolPool manages a pool of intPools.
-type intPoolPool struct {
- pools []*intPool
- lock sync.Mutex
-}
-
-var poolOfIntPools = &intPoolPool{
- pools: make([]*intPool, 0, poolDefaultCap),
-}
-
-// get is looking for an available pool to return.
-func (ipp *intPoolPool) get() *intPool {
- ipp.lock.Lock()
- defer ipp.lock.Unlock()
-
- if len(poolOfIntPools.pools) > 0 {
- ip := ipp.pools[len(ipp.pools)-1]
- ipp.pools = ipp.pools[:len(ipp.pools)-1]
- return ip
- }
- return newIntPool()
-}
-
-// put a pool that has been allocated with get.
-func (ipp *intPoolPool) put(ip *intPool) {
- ipp.lock.Lock()
- defer ipp.lock.Unlock()
-
- if len(ipp.pools) < cap(ipp.pools) {
- ipp.pools = append(ipp.pools, ip)
- }
-}
diff --git a/core/vm/intpool_test.go b/core/vm/intpool_test.go
deleted file mode 100644
index 6c0d00f3ce..0000000000
--- a/core/vm/intpool_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2018 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package vm
-
-import (
- "testing"
-)
-
-func TestIntPoolPoolGet(t *testing.T) {
- poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
-
- nip := poolOfIntPools.get()
- if nip == nil {
- t.Fatalf("Invalid pool allocation")
- }
-}
-
-func TestIntPoolPoolPut(t *testing.T) {
- poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
-
- nip := poolOfIntPools.get()
- if len(poolOfIntPools.pools) != 0 {
- t.Fatalf("Pool got added to list when none should have been")
- }
-
- poolOfIntPools.put(nip)
- if len(poolOfIntPools.pools) == 0 {
- t.Fatalf("Pool did not get added to list when one should have been")
- }
-}
-
-func TestIntPoolPoolReUse(t *testing.T) {
- poolOfIntPools.pools = make([]*intPool, 0, poolDefaultCap)
- nip := poolOfIntPools.get()
- poolOfIntPools.put(nip)
- poolOfIntPools.get()
-
- if len(poolOfIntPools.pools) != 0 {
- t.Fatalf("Invalid number of pools. Got %d, expected %d", len(poolOfIntPools.pools), 0)
- }
-}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index dd93ce3ea3..a5baef918f 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -39,7 +39,6 @@ func (s Storage) Copy() Storage {
for key, value := range s {
cpy[key] = value
}
-
return cpy
}
@@ -115,16 +114,16 @@ type Tracer interface {
type StructLogger struct {
cfg LogConfig
- logs []StructLog
- changedValues map[common.Address]Storage
- output []byte
- err error
+ storage map[common.Address]Storage
+ logs []StructLog
+ output []byte
+ err error
}
// NewStructLogger returns a new logger
func NewStructLogger(cfg *LogConfig) *StructLogger {
logger := &StructLogger{
- changedValues: make(map[common.Address]Storage),
+ storage: make(map[common.Address]Storage),
}
if cfg != nil {
logger.cfg = *cfg
@@ -139,28 +138,12 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea
// CaptureState logs a new structured log message and pushes it out to the environment
//
-// CaptureState also tracks SSTORE ops to track dirty values.
+// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
// check if already accumulated the specified number of logs
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
return ErrTraceLimitReached
}
-
- // initialise new changed values storage container for this contract
- // if not present.
- if l.changedValues[contract.Address()] == nil {
- l.changedValues[contract.Address()] = make(Storage)
- }
-
- // capture SSTORE opcodes and determine the changed value and store
- // it in the local storage container.
- if op == SSTORE && stack.len() >= 2 {
- var (
- value = common.BigToHash(stack.data[stack.len()-2])
- address = common.BigToHash(stack.data[stack.len()-1])
- )
- l.changedValues[contract.Address()][address] = value
- }
// Copy a snapshot of the current memory state to a new buffer
var mem []byte
if !l.cfg.DisableMemory {
@@ -172,22 +155,42 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
if !l.cfg.DisableStack {
stck = make([]*big.Int, len(stack.Data()))
for i, item := range stack.Data() {
- stck[i] = new(big.Int).Set(item)
+ stck[i] = new(big.Int).Set(item.ToBig())
}
}
- // Copy a snapshot of the current storage to a new container
- var storage Storage
- if !l.cfg.DisableStorage {
- storage = l.changedValues[contract.Address()].Copy()
- }
var rstack []uint64
if !l.cfg.DisableStack && rStack != nil {
rstck := make([]uint64, len(rStack.data))
copy(rstck, rStack.data)
}
+ // Copy a snapshot of the current storage to a new container
+ var storage Storage
+ if !l.cfg.DisableStorage {
+ // initialise new changed values storage container for this contract
+ // if not present.
+ if l.storage[contract.Address()] == nil {
+ l.storage[contract.Address()] = make(Storage)
+ }
+ // capture SLOAD opcodes and record the read entry in the local storage
+ if op == SLOAD && stack.len() >= 1 {
+ var (
+ address = common.Hash(stack.data[stack.len()-1].Bytes32())
+ value = env.StateDB.GetState(contract.Address(), address)
+ )
+ l.storage[contract.Address()][address] = value
+ }
+ // capture SSTORE opcodes and record the written entry in the local storage.
+ if op == SSTORE && stack.len() >= 2 {
+ var (
+ value = common.Hash(stack.data[stack.len()-2].Bytes32())
+ address = common.Hash(stack.data[stack.len()-1].Bytes32())
+ )
+ l.storage[contract.Address()][address] = value
+ }
+ storage = l.storage[contract.Address()].Copy()
+ }
// create a new snapshot of the EVM.
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, storage, depth, env.StateDB.GetRefund(), err}
-
l.logs = append(l.logs, log)
return nil
}
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index 12b9011548..ad4b8e6bca 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -62,7 +62,12 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
log.Memory = memory.Data()
}
if !l.cfg.DisableStack {
- log.Stack = stack.Data()
+ //TODO(@holiman) improve this
+ logstack := make([]*big.Int, len(stack.Data()))
+ for i, item := range stack.Data() {
+ logstack[i] = item.ToBig()
+ }
+ log.Stack = logstack
log.ReturnStack = rStack.data
}
return l.encoder.Encode(log)
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index c8579a01a7..444854ba2a 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -23,6 +23,7 @@ import (
"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/core/state"
"github.com/celo-org/celo-blockchain/params"
+ "github.com/holiman/uint256"
)
type dummyContractRef struct {
@@ -57,15 +58,15 @@ func TestStoreCapture(t *testing.T) {
rstack = newReturnStack()
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
)
- stack.push(big.NewInt(1))
- stack.push(big.NewInt(0))
+ stack.push(uint256.NewInt().SetUint64(1))
+ stack.push(uint256.NewInt())
var index common.Hash
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil)
- if len(logger.changedValues[contract.Address()]) == 0 {
- t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.changedValues[contract.Address()]))
+ if len(logger.storage[contract.Address()]) == 0 {
+ t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
}
exp := common.BigToHash(big.NewInt(1))
- if logger.changedValues[contract.Address()][index] != exp {
- t.Errorf("expected %x, got %x", exp, logger.changedValues[contract.Address()][index])
+ if logger.storage[contract.Address()][index] != exp {
+ t.Errorf("expected %x, got %x", exp, logger.storage[contract.Address()][index])
}
}
diff --git a/core/vm/memory.go b/core/vm/memory.go
index 7c9797751e..ba5f8485dc 100644
--- a/core/vm/memory.go
+++ b/core/vm/memory.go
@@ -18,9 +18,8 @@ package vm
import (
"fmt"
- "math/big"
- "github.com/celo-org/celo-blockchain/common/math"
+ "github.com/holiman/uint256"
)
// Memory implements a simple memory model for the ethereum virtual machine.
@@ -50,7 +49,7 @@ func (m *Memory) Set(offset, size uint64, value []byte) {
// Set32 sets the 32 bytes starting at offset to the value of val, left-padded with zeroes to
// 32 bytes.
-func (m *Memory) Set32(offset uint64, val *big.Int) {
+func (m *Memory) Set32(offset uint64, val *uint256.Int) {
// length of store may never be less than offset + size.
// The store should be resized PRIOR to setting the memory
if offset+32 > uint64(len(m.store)) {
@@ -59,7 +58,7 @@ func (m *Memory) Set32(offset uint64, val *big.Int) {
// Zero the memory area
copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
// Fill in relevant bits
- math.ReadBits(val, m.store[offset:offset+32])
+ val.WriteToSlice(m.store[offset:])
}
// Resize resizes the memory to size
diff --git a/core/vm/stack.go b/core/vm/stack.go
index 0171ad0dbd..99de4d79c8 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -18,36 +18,36 @@ package vm
import (
"fmt"
- "math/big"
+
+ "github.com/holiman/uint256"
)
// Stack is an object for basic stack operations. Items popped to the stack are
// expected to be changed and modified. stack does not take care of adding newly
// initialised objects.
type Stack struct {
- data []*big.Int
+ data []uint256.Int
}
func newstack() *Stack {
- return &Stack{data: make([]*big.Int, 0, 1024)}
+ return &Stack{data: make([]uint256.Int, 0, 16)}
}
-// Data returns the underlying big.Int array.
-func (st *Stack) Data() []*big.Int {
+// Data returns the underlying uint256.Int array.
+func (st *Stack) Data() []uint256.Int {
return st.data
}
-func (st *Stack) push(d *big.Int) {
+func (st *Stack) push(d *uint256.Int) {
// NOTE push limit (1024) is checked in baseCheck
- //stackItem := new(big.Int).Set(d)
- //st.data = append(st.data, stackItem)
- st.data = append(st.data, d)
+ st.data = append(st.data, *d)
}
-func (st *Stack) pushN(ds ...*big.Int) {
+func (st *Stack) pushN(ds ...uint256.Int) {
+ // FIXME: Is there a way to pass args by pointers.
st.data = append(st.data, ds...)
}
-func (st *Stack) pop() (ret *big.Int) {
+func (st *Stack) pop() (ret uint256.Int) {
ret = st.data[len(st.data)-1]
st.data = st.data[:len(st.data)-1]
return
@@ -61,17 +61,17 @@ func (st *Stack) swap(n int) {
st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
}
-func (st *Stack) dup(pool *intPool, n int) {
- st.push(pool.get().Set(st.data[st.len()-n]))
+func (st *Stack) dup(n int) {
+ st.push(&st.data[st.len()-n])
}
-func (st *Stack) peek() *big.Int {
- return st.data[st.len()-1]
+func (st *Stack) peek() *uint256.Int {
+ return &st.data[st.len()-1]
}
// Back returns the n'th item in stack
-func (st *Stack) Back(n int) *big.Int {
- return st.data[st.len()-n-1]
+func (st *Stack) Back(n int) *uint256.Int {
+ return &st.data[st.len()-n-1]
}
// Print dumps the content of the stack
diff --git a/crypto/crypto.go b/crypto/crypto.go
index 8c4870e252..6aad31c4b1 100644
--- a/crypto/crypto.go
+++ b/crypto/crypto.go
@@ -24,6 +24,7 @@ import (
"encoding/hex"
"errors"
"fmt"
+ "hash"
"io"
"io/ioutil"
"math/big"
@@ -51,23 +52,33 @@ var (
var errInvalidPubkey = errors.New("invalid secp256k1 public key")
+// KeccakState wraps sha3.state. In addition to the usual hash methods, it also supports
+// Read to get a variable amount of data from the hash state. Read is faster than Sum
+// because it doesn't copy the internal state, but also modifies the internal state.
+type KeccakState interface {
+ hash.Hash
+ Read([]byte) (int, error)
+}
+
// Keccak256 calculates and returns the Keccak256 hash of the input data.
func Keccak256(data ...[]byte) []byte {
- d := sha3.NewLegacyKeccak256()
+ b := make([]byte, 32)
+ d := sha3.NewLegacyKeccak256().(KeccakState)
for _, b := range data {
d.Write(b)
}
- return d.Sum(nil)
+ d.Read(b)
+ return b
}
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
// converting it to an internal Hash data structure.
func Keccak256Hash(data ...[]byte) (h common.Hash) {
- d := sha3.NewLegacyKeccak256()
+ d := sha3.NewLegacyKeccak256().(KeccakState)
for _, b := range data {
d.Write(b)
}
- d.Sum(h[:0])
+ d.Read(h[:])
return h
}
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index 35d0eef34a..9a7c06d7ce 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -8,10 +8,19 @@ package secp256k1
/*
#cgo CFLAGS: -I./libsecp256k1
#cgo CFLAGS: -I./libsecp256k1/src/
+
+#ifdef __SIZEOF_INT128__
+# define HAVE___INT128
+# define USE_FIELD_5X52
+# define USE_SCALAR_4X64
+#else
+# define USE_FIELD_10X26
+# define USE_SCALAR_8X32
+#endif
+
+#define USE_ENDOMORPHISM
#define USE_NUM_NONE
-#define USE_FIELD_10X26
#define USE_FIELD_INV_BUILTIN
-#define USE_SCALAR_8X32
#define USE_SCALAR_INV_BUILTIN
#define NDEBUG
#include "./libsecp256k1/src/secp256k1.c"
diff --git a/eth/api_backend.go b/eth/api_backend.go
index fd18e365d0..4bb2f21d62 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -292,10 +292,14 @@ func (b *EthAPIBackend) ExtRPCEnabled() bool {
return b.extRPCEnabled
}
-func (b *EthAPIBackend) RPCGasCap() *big.Int {
+func (b *EthAPIBackend) RPCGasCap() uint64 {
return b.eth.config.RPCGasCap
}
+func (b *EthAPIBackend) RPCTxFeeCap() float64 {
+ return b.eth.config.RPCTxFeeCap
+}
+
func (b *EthAPIBackend) BloomStatus() (uint64, uint64) {
sections, _, _ := b.eth.bloomIndexer.Sections()
return params.BloomBitsBlocks, sections
diff --git a/eth/api_tracer.go b/eth/api_tracer.go
index e342545b77..09dd4dca71 100644
--- a/eth/api_tracer.go
+++ b/eth/api_tracer.go
@@ -765,10 +765,15 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
// Depending on the tracer type, format and return the output
switch tracer := tracer.(type) {
case *vm.StructLogger:
+ // If the result contains a revert reason, return it.
+ returnVal := fmt.Sprintf("%x", result.Return())
+ if len(result.Revert()) > 0 {
+ returnVal = fmt.Sprintf("%x", result.Revert())
+ }
return ðapi.ExecutionResult{
Gas: result.UsedGas,
Failed: result.Failed(),
- ReturnValue: fmt.Sprintf("%x", result.Return()),
+ ReturnValue: returnVal,
StructLogs: ethapi.FormatLogs(tracer.StructLogs()),
}, nil
diff --git a/eth/config.go b/eth/config.go
index d1ea1d9932..d05ccc214f 100644
--- a/eth/config.go
+++ b/eth/config.go
@@ -42,7 +42,9 @@ var DefaultConfig = Config{
SnapshotCache: 102,
GatewayFee: big.NewInt(0),
- TxPool: core.DefaultTxPoolConfig,
+ TxPool: core.DefaultTxPoolConfig,
+ RPCGasCap: 25000000,
+ RPCTxFeeCap: 500, // 500 celo
Istanbul: *istanbul.DefaultConfig,
}
@@ -121,7 +123,11 @@ type Config struct {
EVMInterpreter string
// RPCGasCap is the global gas cap for eth-call variants.
- RPCGasCap *big.Int `toml:",omitempty"`
+ RPCGasCap uint64 `toml:",omitempty"`
+
+ // RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for
+ // send-transction variants. The unit is ether.
+ RPCTxFeeCap float64 `toml:",omitempty"`
// Checkpoint is a hardcoded checkpoint which can be nil.
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 47cdc36783..e06261edec 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -108,7 +108,7 @@ type Downloader struct {
rttEstimate uint64 // Round trip time to target for download requests
rttConfidence uint64 // Confidence in the estimated RTT (unit: millionths to allow atomic ops)
- Mode SyncMode // Synchronisation mode defining the strategy used (per sync cycle)
+ mode uint32 // Synchronisation mode defining the strategy used (per sync cycle), use d.getMode() to get the SyncMode
mux *event.TypeMux // Event multiplexer to announce sync operation events
checkpoint uint64 // Checkpoint block number to enforce head against (e.g. fast sync)
@@ -287,15 +287,16 @@ func (d *Downloader) Progress() ethereum.SyncProgress {
defer d.syncStatsLock.RUnlock()
current := uint64(0)
+ mode := d.getMode()
switch {
- case d.blockchain != nil && d.Mode == FullSync:
+ case d.blockchain != nil && mode == FullSync:
current = d.blockchain.CurrentBlock().NumberU64()
- case d.blockchain != nil && d.Mode == FastSync:
+ case d.blockchain != nil && mode == FastSync:
current = d.blockchain.CurrentFastBlock().NumberU64()
case d.lightchain != nil:
current = d.lightchain.CurrentHeader().Number.Uint64()
default:
- log.Error("Unknown downloader chain/mode combo", "light", d.lightchain != nil, "full", d.blockchain != nil, "mode", d.Mode)
+ log.Error("Unknown downloader chain/mode combo", "light", d.lightchain != nil, "full", d.blockchain != nil, "mode", mode)
}
log.Debug(fmt.Sprintf("Current head is %v", current))
return ethereum.SyncProgress{
@@ -357,7 +358,9 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
return err
}
- if errors.Is(err, errInvalidChain) {
+ if errors.Is(err, errInvalidChain) || errors.Is(err, errBadPeer) || errors.Is(err, errTimeout) ||
+ errors.Is(err, errStallingPeer) || errors.Is(err, errUnsyncedPeer) || errors.Is(err, errEmptyHeaderSet) ||
+ errors.Is(err, errPeersUnavailable) || errors.Is(err, errTooOld) || errors.Is(err, errInvalidAncestor) {
log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
if d.dropPeer == nil {
// The dropPeer method is nil when `--copydb` is used for a local copy.
@@ -368,22 +371,7 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
}
return err
}
-
- switch err {
- case errTimeout, errBadPeer, errStallingPeer, errUnsyncedPeer,
- errEmptyHeaderSet, errPeersUnavailable, errTooOld,
- errInvalidAncestor:
- log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
- if d.dropPeer == nil {
- // The dropPeer method is nil when `--copydb` is used for a local copy.
- // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
- log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", id)
- } else {
- d.dropPeer(id)
- }
- default:
- log.Warn("Synchronisation failed, retrying", "err", err)
- }
+ log.Warn("Synchronisation failed, retrying", "err", err)
return err
}
@@ -445,8 +433,8 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
defer d.Cancel() // No matter what, we can't leave the cancel channel open
- // Set the requested sync mode, unless it's forbidden
- d.Mode = mode
+ // Atomically set the requested sync mode
+ atomic.StoreUint32(&d.mode, uint32(mode))
// Retrieve the origin peer and initiate the downloading process
p := d.peers.Peer(id)
@@ -456,6 +444,10 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
return d.syncWithPeer(p, hash, td)
}
+func (d *Downloader) getMode() SyncMode {
+ return SyncMode(atomic.LoadUint32(&d.mode))
+}
+
// syncWithPeer starts a block synchronization based on the hash chain from the
// specified peer and head hash.
func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int) (err error) {
@@ -472,8 +464,9 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
if p.version < 62 {
return errTooOld
}
+ mode := d.getMode()
- log.Debug("Synchronising with the network", "peer", p.id, "eth", p.version, "head", hash, "td", td, "mode", d.Mode)
+ log.Debug("Synchronising with the network", "peer", p.id, "eth", p.version, "head", hash, "td", td, "mode", mode)
defer func(start time.Time) {
log.Debug("Synchronisation terminated", "elapsed", common.PrettyDuration(time.Since(start)))
}(time.Now())
@@ -499,7 +492,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
// Ensure our origin point is below any fast sync pivot point
pivot := uint64(0)
- if d.Mode == FastSync {
+ if mode == FastSync {
pivot = d.calcPivot(height)
rawdb.WriteLastPivotNumber(d.stateDB, pivot)
if pivot == 0 {
@@ -509,10 +502,10 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
}
}
d.committed = 1
- if d.Mode == FastSync && pivot != 0 {
+ if mode == FastSync && pivot != 0 {
d.committed = 0
}
- if d.Mode == FastSync {
+ if mode == FastSync {
// Set the ancient data limitation.
// If we are running fast sync, all block data older than ancientLimit will be
// written to the ancient store. More recent data will be written to the active
@@ -550,7 +543,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
}
}
// Initiate the sync using a concurrent header and content retrieval algorithm
- d.queue.Prepare(origin+1, d.Mode)
+ d.queue.Prepare(origin+1, mode)
if d.syncInitHook != nil {
d.syncInitHook(origin, height)
}
@@ -560,9 +553,9 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
func() error { return d.processHeaders(origin+1, pivot, td) },
}
- if d.Mode == FastSync {
+ if mode == FastSync {
fetchers = append(fetchers, func() error { return d.processFastSyncContent(latest) })
- } else if d.Mode == FullSync {
+ } else if mode == FullSync {
fetchers = append(fetchers, d.processFullSyncContent)
}
return d.spawnSync(fetchers)
@@ -650,6 +643,7 @@ func (d *Downloader) fetchHeight(p *peerConnection) (*types.Header, error) {
ttl := d.requestTTL()
timeout := time.After(ttl)
+ mode := d.getMode()
for {
select {
case <-d.cancelCh:
@@ -665,10 +659,10 @@ func (d *Downloader) fetchHeight(p *peerConnection) (*types.Header, error) {
headers := packet.(*headerPack).headers
if len(headers) != 1 {
p.log.Debug("Multiple headers for single request", "headers", len(headers))
- return nil, errBadPeer
+ return nil, fmt.Errorf("%w: multiple headers (%d) for single request", errBadPeer, len(headers))
}
head := headers[0]
- if (d.Mode == FastSync || d.Mode == LightSync) && head.Number.Uint64() < d.checkpoint {
+ if (mode == FastSync || mode == LightSync) && head.Number.Uint64() < d.checkpoint {
p.log.Warn("Remote head below checkpoint", "number", head.Number, "hash", head.Hash())
return nil, errUnsyncedPeer
}
@@ -750,7 +744,8 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header)
localHeight uint64
remoteHeight = remoteHeader.Number.Uint64()
)
- switch d.Mode {
+ mode := d.getMode()
+ switch mode {
case FullSync:
localHeight = d.blockchain.CurrentBlock().NumberU64()
case FastSync:
@@ -770,7 +765,7 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header)
// If we're doing a light sync, ensure the floor doesn't go below the CHT, as
// all headers before that point will be missing.
- if !d.Mode.SyncFullBlockChain() {
+ if !mode.SyncFullBlockChain() {
// If we don't know the current CHT position, find it
if d.genesis == 0 {
header := d.lightchain.CurrentHeader()
@@ -836,7 +831,7 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header)
n := headers[i].Number.Uint64()
var known bool
- switch d.Mode {
+ switch mode {
case FullSync:
known = d.blockchain.HasBlock(h, n)
case FastSync:
@@ -900,7 +895,7 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header)
headers := packer.(*headerPack).headers
if len(headers) != 1 {
p.log.Debug("Multiple headers for single request", "headers", len(headers))
- return 0, errBadPeer
+ return 0, fmt.Errorf("%w: multiple headers (%d) for single request", errBadPeer, len(headers))
}
arrived = true
@@ -909,7 +904,7 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header)
n := headers[0].Number.Uint64()
var known bool
- switch d.Mode {
+ switch mode {
case FullSync:
known = d.blockchain.HasBlock(h, n)
case FastSync:
@@ -924,7 +919,7 @@ func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header)
header := d.lightchain.GetHeaderByHash(h) // Independent of sync mode, header surely exists
if header.Number.Uint64() != check {
p.log.Debug("Received non requested header", "number", header.Number, "hash", header.Hash(), "request", check)
- return 0, errBadPeer
+ return 0, fmt.Errorf("%w: non-requested header (%d)", errBadPeer, header.Number)
}
start = check
hash = h
@@ -987,8 +982,9 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64,
}
}
+ mode := d.getMode()
getEpochHeaders := func(fromEpochBlock uint64) {
- if d.Mode != LightestSync {
+ if mode != LightestSync {
panic("This method should be called only in LightestSync mode")
}
if fromEpochBlock%epoch != 0 {
@@ -1037,7 +1033,7 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64,
// Start pulling the header chain skeleton until all is done
ancestor := from
- if d.Mode == LightestSync {
+ if mode == LightestSync {
if epoch == 0 {
panic("Epoch cannot be 0 in IBFT + LightestSync")
}
@@ -1114,7 +1110,7 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64,
if n := len(headers); n > 0 {
// Retrieve the current head we're at
var head uint64
- if d.Mode == LightSync {
+ if mode == LightSync {
head = d.lightchain.CurrentHeader().Number.Uint64()
} else {
head = d.blockchain.CurrentFastBlock().NumberU64()
@@ -1149,7 +1145,7 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64,
}
// In all other sync modes, we fetch the block immediately after the current block.
// In the lightest sync mode, increment the value by epoch instead.
- if d.Mode == LightestSync {
+ if mode == LightestSync {
lastFetchedHeaderNumber := headers[len(headers)-1].Number.Uint64()
moreHeaderFetchesPending := getEpochOrNormalHeaders(lastFetchedHeaderNumber + 1)
if !moreHeaderFetchesPending {
@@ -1171,7 +1167,7 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64,
p.log.Trace("All headers delayed, waiting")
select {
case <-time.After(fsHeaderContCheck):
- if d.Mode == LightestSync {
+ if mode == LightestSync {
getEpochOrNormalHeaders(from)
} else {
getHeaders(from)
@@ -1205,7 +1201,7 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64,
case d.headerProcCh <- nil:
case <-d.cancelCh:
}
- return errBadPeer
+ return fmt.Errorf("%w: header request timed out", errBadPeer)
}
}
}
@@ -1499,12 +1495,12 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
var (
rollback uint64 // Zero means no rollback (fine as you can't unroll the genesis)
rollbackErr error
- mode = d.Mode
+ mode = d.getMode()
)
defer func() {
if rollback > 0 {
lastHeader, lastFastBlock, lastBlock := d.lightchain.CurrentHeader().Number, common.Big0, common.Big0
- if d.Mode.SyncFullBlockChain() {
+ if mode.SyncFullBlockChain() {
lastFastBlock = d.blockchain.CurrentFastBlock().Number()
lastBlock = d.blockchain.CurrentBlock().Number()
}
@@ -1513,7 +1509,7 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
log.Error("Failed to roll back chain segment", "head", rollback-1, "err", err)
}
curFastBlock, curBlock := common.Big0, common.Big0
- if d.Mode.SyncFullBlockChain() {
+ if mode.SyncFullBlockChain() {
curFastBlock = d.blockchain.CurrentFastBlock().Number()
curBlock = d.blockchain.CurrentBlock().Number()
}
@@ -1554,7 +1550,7 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
// L: Sync begins, and finds common ancestor at 11
// L: Request new headers up from 11 (R's TD was higher, it must have something)
// R: Nothing to give
- if d.Mode.SyncFullBlockChain() {
+ if mode.SyncFullBlockChain() {
head := d.blockchain.CurrentBlock()
if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())) > 0 {
rollbackErr = errStallingPeer
@@ -1568,8 +1564,7 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
// This check cannot be executed "as is" for full imports, since blocks may still be
// queued for processing when the header download completes. However, as long as the
// peer gave us something useful, we're already happy/progressed (above check).
-
- if d.Mode == FastSync || d.Mode == LightSync {
+ if mode == FastSync || mode == LightSync {
head := d.lightchain.CurrentHeader()
if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 {
rollbackErr = errStallingPeer
@@ -1598,13 +1593,13 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
chunk := headers[:limit]
// In case of header only syncing, validate the chunk immediately
- if mode == FastSync || !mode.SyncFullBlockChain() {
+ if mode == FastSync || !mode.SyncFullBlockChain() { // mode != FullSync ?
// If we're importing pure headers, verify based on their recentness
frequency := fsHeaderCheckFrequency
if chunk[len(chunk)-1].Number.Uint64()+uint64(fsHeaderForceVerify) > pivot {
frequency = 1
}
- if n, err := d.lightchain.InsertHeaderChain(chunk, frequency, d.Mode.SyncFullHeaderChain()); err != nil {
+ if n, err := d.lightchain.InsertHeaderChain(chunk, frequency, mode.SyncFullHeaderChain()); err != nil {
rollbackErr = err
// If some headers were inserted, track them as uncertain
@@ -1623,7 +1618,7 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
}
}
// Unless we're doing light chains, schedule the headers for associated content retrieval
- if d.Mode.SyncFullBlockChain() {
+ if mode.SyncFullBlockChain() {
// If we've reached the allowed number of pending headers, stall a bit
for d.queue.PendingBlocks() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
select {
@@ -1638,7 +1633,7 @@ func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) er
if len(inserts) != len(chunk) {
log.Debug("Stale headers")
rollbackErr = errBadPeer
- return errBadPeer
+ return fmt.Errorf("%w: stale headers", errBadPeer)
}
}
headers = headers[limit:]
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index 08faff947e..d5ffa0f25a 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -517,7 +517,7 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
blocks += length - common
receipts += length - common
}
- if tester.downloader.Mode == LightSync {
+ if tester.downloader.getMode() == LightSync {
blocks, receipts = 1, 1
}
if hs := len(tester.ownHeaders) + len(tester.ancientHeaders) - 1; hs != headers {
diff --git a/eth/downloader/modes.go b/eth/downloader/modes.go
index accdfbdf1a..4ca00f3894 100644
--- a/eth/downloader/modes.go
+++ b/eth/downloader/modes.go
@@ -19,7 +19,8 @@ package downloader
import "fmt"
// SyncMode represents the synchronisation mode of the downloader.
-type SyncMode int
+// It is a uint32 as it is used with atomic operations.
+type SyncMode uint32
const (
FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks
diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go
index 91ca343027..b827a32705 100644
--- a/eth/downloader/peer.go
+++ b/eth/downloader/peer.go
@@ -421,7 +421,7 @@ func (ps *peerSet) Unregister(id string) error {
ps.lock.Lock()
p, ok := ps.peers[id]
if !ok {
- defer ps.lock.Unlock()
+ ps.lock.Unlock()
return errNotRegistered
}
delete(ps.peers, id)
diff --git a/eth/downloader/statesync.go b/eth/downloader/statesync.go
index 069546a8ca..9276060330 100644
--- a/eth/downloader/statesync.go
+++ b/eth/downloader/statesync.go
@@ -63,6 +63,10 @@ func (d *Downloader) syncState(root common.Hash) *stateSync {
s := newStateSync(d, root)
select {
case d.stateSyncStart <- s:
+ // If we tell the statesync to restart with a new root, we also need
+ // to wait for it to actually also start -- when old requests have timed
+ // out or been delivered
+ <-s.started
case <-d.quitCh:
s.err = errCancelStateFetch
close(s.done)
@@ -95,15 +99,9 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync {
finished []*stateReq // Completed or failed requests
timeout = make(chan *stateReq) // Timed out active requests
)
- defer func() {
- // Cancel active request timers on exit. Also set peers to idle so they're
- // available for the next sync.
- for _, req := range active {
- req.timer.Stop()
- req.peer.SetNodeDataIdle(len(req.items))
- }
- }()
+
// Run the state sync.
+ log.Trace("State sync starting", "root", s.root)
go s.run()
defer s.Cancel()
@@ -126,9 +124,11 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync {
select {
// The stateSync lifecycle:
case next := <-d.stateSyncStart:
+ d.spindownStateSync(active, finished, timeout, peerDrop)
return next
case <-s.done:
+ d.spindownStateSync(active, finished, timeout, peerDrop)
return nil
// Send the next finished request to the current sync:
@@ -189,11 +189,9 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync {
// causes valid requests to go missing and sync to get stuck.
if old := active[req.peer.id]; old != nil {
log.Warn("Busy peer assigned new state fetch", "peer", old.peer.id)
-
- // Make sure the previous one doesn't get siletly lost
+ // Move the previous request to the finished set
old.timer.Stop()
old.dropped = true
-
finished = append(finished, old)
}
// Start a timer to notify the sync loop if the peer stalled.
@@ -210,6 +208,46 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync {
}
}
+// spindownStateSync 'drains' the outstanding requests; some will be delivered and other
+// will time out. This is to ensure that when the next stateSync starts working, all peers
+// are marked as idle and de facto _are_ idle.
+func (d *Downloader) spindownStateSync(active map[string]*stateReq, finished []*stateReq, timeout chan *stateReq, peerDrop chan *peerConnection) {
+ log.Trace("State sync spinning down", "active", len(active), "finished", len(finished))
+
+ for len(active) > 0 {
+ var (
+ req *stateReq
+ reason string
+ )
+ select {
+ // Handle (drop) incoming state packs:
+ case pack := <-d.stateCh:
+ req = active[pack.PeerId()]
+ reason = "delivered"
+ // Handle dropped peer connections:
+ case p := <-peerDrop:
+ req = active[p.id]
+ reason = "peerdrop"
+ // Handle timed-out requests:
+ case req = <-timeout:
+ reason = "timeout"
+ }
+ if req == nil {
+ continue
+ }
+ req.peer.log.Trace("State peer marked idle (spindown)", "req.items", len(req.items), "reason", reason)
+ req.timer.Stop()
+ delete(active, req.peer.id)
+ req.peer.SetNodeDataIdle(len(req.items))
+ }
+ // The 'finished' set contains deliveries that we were going to pass to processing.
+ // Those are now moot, but we still need to set those peers as idle, which would
+ // otherwise have been done after processing
+ for _, req := range finished {
+ req.peer.SetNodeDataIdle(len(req.items))
+ }
+}
+
// stateSync schedules requests for downloading a particular state trie defined
// by a given state root.
type stateSync struct {
@@ -222,11 +260,15 @@ type stateSync struct {
numUncommitted int
bytesUncommitted int
+ started chan struct{} // Started is signalled once the sync loop starts
+
deliver chan *stateReq // Delivery channel multiplexing peer responses
cancel chan struct{} // Channel to signal a termination request
cancelOnce sync.Once // Ensures cancel only ever gets called once
done chan struct{} // Channel to signal termination completion
err error // Any error hit during sync (set before completion)
+
+ root common.Hash
}
// stateTask represents a single trie node download task, containing a set of
@@ -246,6 +288,8 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync {
deliver: make(chan *stateReq),
cancel: make(chan struct{}),
done: make(chan struct{}),
+ started: make(chan struct{}),
+ root: root,
}
}
@@ -276,6 +320,7 @@ func (s *stateSync) Cancel() error {
// pushed here async. The reason is to decouple processing from data receipt
// and timeouts.
func (s *stateSync) loop() (err error) {
+ close(s.started)
// Listen for new peer events to assign tasks to them
newPeer := make(chan *peerConnection, 1024)
peerSub := s.d.peers.SubscribeNewPeers(newPeer)
@@ -331,11 +376,11 @@ func (s *stateSync) loop() (err error) {
}
// Process all the received blobs and check for stale delivery
delivered, err := s.process(req)
+ req.peer.SetNodeDataIdle(delivered)
if err != nil {
log.Warn("Node data write error", "err", err)
return err
}
- req.peer.SetNodeDataIdle(delivered)
}
}
return nil
@@ -372,7 +417,7 @@ func (s *stateSync) assignTasks() {
// If the peer was assigned tasks to fetch, send the network request
if len(req.items) > 0 {
- req.peer.log.Trace("Requesting new batch of data", "type", "state", "count", len(req.items))
+ req.peer.log.Trace("Requesting new batch of data", "type", "state", "count", len(req.items), "root", s.root)
select {
case s.d.trackStateReq <- req:
req.peer.FetchNodeData(req.items)
diff --git a/eth/gen_config.go b/eth/gen_config.go
index ebb0a172a0..f61f431f5a 100644
--- a/eth/gen_config.go
+++ b/eth/gen_config.go
@@ -43,6 +43,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
TrieCleanCache int
TrieDirtyCache int
TrieTimeout time.Duration
+ SnapshotCache int
Miner miner.Config
TxPool core.TxPoolConfig
EnablePreimageRecording bool
@@ -50,7 +51,8 @@ func (c Config) MarshalTOML() (interface{}, error) {
DocRoot string `toml:"-"`
EWASMInterpreter string
EVMInterpreter string
- RPCGasCap *big.Int `toml:",omitempty"`
+ RPCGasCap uint64 `toml:",omitempty"`
+ RPCTxFeeCap float64 `toml:",omitempty"`
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
OverrideChurrito *big.Int `toml:",omitempty"`
@@ -83,6 +85,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.TrieCleanCache = c.TrieCleanCache
enc.TrieDirtyCache = c.TrieDirtyCache
enc.TrieTimeout = c.TrieTimeout
+ enc.SnapshotCache = c.SnapshotCache
enc.Miner = c.Miner
enc.TxPool = c.TxPool
enc.EnablePreimageRecording = c.EnablePreimageRecording
@@ -91,6 +94,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.EWASMInterpreter = c.EWASMInterpreter
enc.EVMInterpreter = c.EVMInterpreter
enc.RPCGasCap = c.RPCGasCap
+ enc.RPCTxFeeCap = c.RPCTxFeeCap
enc.Checkpoint = c.Checkpoint
enc.CheckpointOracle = c.CheckpointOracle
enc.OverrideChurrito = c.OverrideChurrito
@@ -127,6 +131,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
TrieCleanCache *int
TrieDirtyCache *int
TrieTimeout *time.Duration
+ SnapshotCache *int
Miner *miner.Config
TxPool *core.TxPoolConfig
EnablePreimageRecording *bool
@@ -134,7 +139,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
DocRoot *string `toml:"-"`
EWASMInterpreter *string
EVMInterpreter *string
- RPCGasCap *big.Int `toml:",omitempty"`
+ RPCGasCap *uint64 `toml:",omitempty"`
+ RPCTxFeeCap *float64 `toml:",omitempty"`
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
OverrideChurrito *big.Int `toml:",omitempty"`
@@ -222,6 +228,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.TrieTimeout != nil {
c.TrieTimeout = *dec.TrieTimeout
}
+ if dec.SnapshotCache != nil {
+ c.SnapshotCache = *dec.SnapshotCache
+ }
if dec.Miner != nil {
c.Miner = *dec.Miner
}
@@ -244,7 +253,10 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
c.EVMInterpreter = *dec.EVMInterpreter
}
if dec.RPCGasCap != nil {
- c.RPCGasCap = dec.RPCGasCap
+ c.RPCGasCap = *dec.RPCGasCap
+ }
+ if dec.RPCTxFeeCap != nil {
+ c.RPCTxFeeCap = *dec.RPCTxFeeCap
}
if dec.Checkpoint != nil {
c.Checkpoint = dec.Checkpoint
diff --git a/eth/handler_test.go b/eth/handler_test.go
index b32b9bc6f9..df234292f2 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -607,13 +607,16 @@ func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) {
select {
case <-doneCh:
received++
-
- case <-time.After(time.Second):
+ if received > broadcastExpected {
+ // We can bail early here
+ t.Errorf("broadcast count mismatch: have %d > want %d", received, broadcastExpected)
+ return
+ }
+ case <-time.After(2 * time.Second):
if received != broadcastExpected {
t.Errorf("broadcast count mismatch: have %d, want %d", received, broadcastExpected)
}
return
-
case err = <-errCh:
t.Fatalf("broadcast failed: %v", err)
}
diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go
index 4922aca4b0..9bb376698b 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/tracer.go
@@ -162,7 +162,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int {
log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
return new(big.Int)
}
- return sw.stack.Data()[len(sw.stack.Data())-idx-1]
+ return sw.stack.Back(idx).ToBig()
}
// pushObject assembles a JSVM object wrapping a swappable stack and pushes it
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index 13aa1e1bf0..e1b5d7f280 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -354,6 +354,7 @@ func (s *Service) loop(ctx context.Context) {
if err = s.report(conn, sendCh); err != nil {
log.Warn("Initial stats report failed", "err", err)
conn.Close()
+ errTimer.Reset(0)
continue
}
diff --git a/go.mod b/go.mod
index 9fec059b6a..133b39c0e2 100644
--- a/go.mod
+++ b/go.mod
@@ -31,9 +31,10 @@ require (
github.com/google/go-cmp v0.3.1 // indirect
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277
- github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad
+ github.com/hashicorp/golang-lru v0.5.4
github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff
- github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3
+ github.com/holiman/uint256 v1.1.0
+ github.com/huin/goupnp v1.0.0
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21
@@ -51,10 +52,10 @@ require (
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150
github.com/rjeczalik/notify v0.9.1
- github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00
+github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 // indirect
github.com/shopspring/decimal v1.2.0
- github.com/shirou/gopsutil v2.20.5-0.20200531151128-663af789c085+incompatible
+ github.com/shirou/gopsutil v2.20.5+incompatible
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
@@ -70,7 +71,7 @@ require (
golang.org/x/text v0.3.3
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
- gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200603215123-a4a8cb9d2cbc
+ gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
gopkg.in/urfave/cli.v1 v1.20.0
gopkg.in/yaml.v2 v2.2.7 // indirect
gotest.tools v2.2.0+incompatible // indirect
diff --git a/go.sum b/go.sum
index e8895d28e7..77b22bdeca 100644
--- a/go.sum
+++ b/go.sum
@@ -24,6 +24,8 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 h1:5sXbqlSomvdjlRbWyNqkPsJ3Fg+tQZCbgeX1VGljbQY=
+github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
@@ -51,9 +53,6 @@ github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72 h1:fUmDBbSvv
github.com/buraksezer/consistent v0.0.0-20191006190839-693edf70fd72/go.mod h1:OEE5igu/CDjGegM1Jn6ZMo7R6LlV/JChAkjfQQIRLpg=
github.com/celo-org/celo-bls-go v0.2.4 h1:V1y92kM5IRJWQZ6DCwqiKLW7swmUA5y/dPJ9YbU4HfA=
github.com/celo-org/celo-bls-go v0.2.4/go.mod h1:eXUCLXu5F1yfd3M+3VaUk5ZUXaA0sLK2rWdLC1Cfaqo=
-github.com/celo-org/mobile v0.0.0-20201127114005-6a1221213dcf h1:vjXHjR4gf41rdPgg3XafcYgpxrj28zIAZ89KDTStL6U=
-github.com/celo-org/mobile v0.0.0-20201127114005-6a1221213dcf/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
-github.com/celo-org/mobile v0.0.0-20210324213558-66ac87d7fb95 h1:X0tWCNjfHVI3NkXhfHPXdQA/Hdi4XexGDER9QPh3V9A=
github.com/celo-org/mobile v0.0.0-20210324213558-66ac87d7fb95/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
@@ -91,6 +90,8 @@ github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
+github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug=
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
@@ -99,7 +100,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c h1:zqAKixg3cTcIasAMJV+EcfVbWwLpOZ7LeoWJvcuD/5Q=
github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -109,14 +109,17 @@ github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989 h1:giknQ4mEuDF
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277 h1:E0whKxgp2ojts0FDgUA8dl62bmH0LxKanMoBr6MDTDM=
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
-github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad h1:eMxs9EL0PvIGS9TTtxg4R+JxuPGav82J8rA+GFnY7po=
-github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff h1:LeVKjw8pcDQj7WVVnbFvbD7ovcv+r/l15ka1NH6Lswc=
github.com/hdevalence/ed25519consensus v0.0.0-20201207055737-7fde80a9d5ff/go.mod h1:Feit0l8NcNO4g69XNjwvsR0LGcwMMfzI1TF253rOIlQ=
+github.com/holiman/uint256 v1.1.0 h1:Iye6ze0DW9s+7EMn8y6Q4ebegDzpu28JQHEVM1Bq+Wg=
+github.com/holiman/uint256 v1.1.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3 h1:DqD8eigqlUm0+znmx7zhL0xvTW3+e1jCekJMfBUADWI=
-github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
+github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
+github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
+github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883 h1:FSeK4fZCo8u40n2JMnyAsd6x7+SbvoOMHvQOU/n10P4=
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA=
@@ -189,13 +192,12 @@ github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9Ac
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521 h1:3hxavr+IHMsQBrYUPQM5v0CgENFktkkbg1sfpgM3h20=
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I=
+github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/shirou/gopsutil v2.20.5-0.20200531151128-663af789c085+incompatible h1:+gAR1bMhuoQnZMTWFIvp7ukynULPsteLzG+siZKLtD8=
-github.com/shirou/gopsutil v2.20.5-0.20200531151128-663af789c085+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg=
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570 h1:gIlAHnH1vJb5vwEjIp5kBj/eu99p/bl0Ay2goiPe5xE=
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
@@ -211,15 +213,12 @@ github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJ
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 h1:1cngl9mPEoITZG8s8cVcUy5CeIBYhEESkOB7m6Gmkrk=
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
-golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
@@ -227,9 +226,9 @@ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMx
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd h1:ePuNC7PZ6O5BzgPn9bZayERXBdfZjUYoXEf5BTfDfh8=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -244,6 +243,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
@@ -256,10 +256,8 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69 h1:yBHHx+XZqXJBm6Exke3N7V9gnlsyXxoCPEb1yVenjfk=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -269,8 +267,8 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
-gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200603215123-a4a8cb9d2cbc h1:17cdygvFw3DEyNMh81Bk687W74d5pcC5qEKQICv9N6g=
-gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200603215123-a4a8cb9d2cbc/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
+gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 h1:a6cXbcDDUkSBlpnkWV1bJ+vv3mOgQEltEJ2rPxroVu0=
+gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
diff --git a/internal/build/azure.go b/internal/build/azure.go
index 7862842650..9c9cc2dcc5 100644
--- a/internal/build/azure.go
+++ b/internal/build/azure.go
@@ -26,7 +26,7 @@ import (
)
// AzureBlobstoreConfig is an authentication and configuration struct containing
-// the data needed by the Azure SDK to interact with a speicifc container in the
+// the data needed by the Azure SDK to interact with a specific container in the
// blobstore.
type AzureBlobstoreConfig struct {
Account string // Account name to authorize API requests with
@@ -71,26 +71,35 @@ func AzureBlobstoreUpload(path string, name string, config AzureBlobstoreConfig)
// AzureBlobstoreList lists all the files contained within an azure blobstore.
func AzureBlobstoreList(config AzureBlobstoreConfig) ([]azblob.BlobItem, error) {
- credential, err := azblob.NewSharedKeyCredential(config.Account, config.Token)
- if err != nil {
- return nil, err
+ credential := azblob.NewAnonymousCredential()
+ if len(config.Token) > 0 {
+ c, err := azblob.NewSharedKeyCredential(config.Account, config.Token)
+ if err != nil {
+ return nil, err
+ }
+ credential = c
}
-
pipeline := azblob.NewPipeline(credential, azblob.PipelineOptions{})
u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net", config.Account))
service := azblob.NewServiceURL(*u, pipeline)
+ var allBlobs []azblob.BlobItem
// List all the blobs from the container and return them
container := service.NewContainerURL(config.Container)
+ nextMarker := azblob.Marker{}
+ for nextMarker.NotDone() {
+ res, err := container.ListBlobsFlatSegment(context.Background(), nextMarker, azblob.ListBlobsSegmentOptions{
+ MaxResults: 5000, // The server only gives max 5K items
+ })
+ if err != nil {
+ return nil, err
+ }
+ allBlobs = append(allBlobs, res.Segment.BlobItems...)
+ nextMarker = res.NextMarker
- res, err := container.ListBlobsFlatSegment(context.Background(), azblob.Marker{}, azblob.ListBlobsSegmentOptions{
- MaxResults: 1024 * 1024 * 1024, // Yes, fetch all of them
- })
- if err != nil {
- return nil, err
}
- return res.Segment.BlobItems, nil
+ return allBlobs, nil
}
// AzureBlobstoreDelete iterates over a list of files to delete and removes them
@@ -121,6 +130,7 @@ func AzureBlobstoreDelete(config AzureBlobstoreConfig, blobs []azblob.BlobItem)
if _, err := blockblob.Delete(context.Background(), azblob.DeleteSnapshotsOptionInclude, azblob.BlobAccessConditions{}); err != nil {
return err
}
+ fmt.Printf("deleted %s (%s)\n", blob.Name, blob.Properties.LastModified)
}
return nil
}
diff --git a/internal/debug/flags.go b/internal/debug/flags.go
index 9bf2ed3e90..8c96c8538b 100644
--- a/internal/debug/flags.go
+++ b/internal/debug/flags.go
@@ -235,7 +235,9 @@ func Setup(ctx *cli.Context) error {
}
address := fmt.Sprintf("%s:%d", listenHost, port)
- StartPProf(address)
+ // This context value ("metrics.addr") represents the utils.MetricsHTTPFlag.Name.
+ // It cannot be imported because it will cause a cyclical dependency.
+ StartPProf(address, !ctx.GlobalIsSet("metrics.addr"))
}
return nil
}
@@ -305,10 +307,12 @@ func getConsoleLogFormat(consoleFormat string, usecolor bool) log.Format {
panic(fmt.Sprintf("Unexpected value for \"%s\" flag: \"%s\"", consoleFormatFlag.Name, consoleFormat))
}
-func StartPProf(address string) {
+func StartPProf(address string, withMetrics bool) {
// Hook go-metrics into expvar on any /debug/metrics request, load all vars
// from the registry into expvar, and execute regular expvar handler.
- exp.Exp(metrics.DefaultRegistry)
+ if withMetrics {
+ exp.Exp(metrics.DefaultRegistry)
+ }
http.Handle("/memsize/", http.StripPrefix("/memsize", &Memsize))
log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address))
go func() {
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index b0d112de14..83c0d4d39b 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -282,7 +282,11 @@ func (s *PrivateAccountAPI) DeriveAccount(url string, path string, pin *bool) (a
// NewAccount will create a new account and returns the address for the new account.
func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) {
- acc, err := fetchKeystore(s.am).NewAccount(password)
+ ks, err := fetchKeystore(s.am)
+ if err != nil {
+ return common.Address{}, err
+ }
+ acc, err := ks.NewAccount(password)
if err == nil {
log.Info("Your new key was generated", "address", acc.Address)
log.Warn("Please backup your key file!", "path", acc.URL.Path)
@@ -293,8 +297,11 @@ func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error)
}
// fetchKeystore retrieves the encrypted keystore from the account manager.
-func fetchKeystore(am *accounts.Manager) *keystore.KeyStore {
- return am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
+func fetchKeystore(am *accounts.Manager) (*keystore.KeyStore, error) {
+ if ks := am.Backends(keystore.KeyStoreType); len(ks) > 0 {
+ return ks[0].(*keystore.KeyStore), nil
+ }
+ return nil, errors.New("local keystore not used")
}
// ImportRawKey stores the given hex encoded ECDSA key into the key directory,
@@ -304,7 +311,11 @@ func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (commo
if err != nil {
return common.Address{}, err
}
- acc, err := fetchKeystore(s.am).ImportECDSA(key, password)
+ ks, err := fetchKeystore(s.am)
+ if err != nil {
+ return common.Address{}, err
+ }
+ acc, err := ks.ImportECDSA(key, password)
return acc.Address, err
}
@@ -328,7 +339,11 @@ func (s *PrivateAccountAPI) UnlockAccount(ctx context.Context, addr common.Addre
} else {
d = time.Duration(*duration) * time.Second
}
- err := fetchKeystore(s.am).TimedUnlock(accounts.Account{Address: addr}, password, d)
+ ks, err := fetchKeystore(s.am)
+ if err != nil {
+ return false, err
+ }
+ err = ks.TimedUnlock(accounts.Account{Address: addr}, password, d)
if err != nil {
log.Warn("Failed account unlock attempt", "address", addr, "err", err)
}
@@ -337,7 +352,10 @@ func (s *PrivateAccountAPI) UnlockAccount(ctx context.Context, addr common.Addre
// LockAccount will lock the account associated with the given address when it's unlocked.
func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
- return fetchKeystore(s.am).Lock(addr) == nil
+ if ks, err := fetchKeystore(s.am); err == nil {
+ return ks.Lock(addr) == nil
+ }
+ return false
}
// signTransaction sets defaults and signs the given transaction
@@ -698,7 +716,7 @@ type CallArgs struct {
}
// ToMessage converts CallArgs to the Message type used by the core evm
-func (args *CallArgs) ToMessage(globalGasCap *big.Int) types.Message {
+func (args *CallArgs) ToMessage(globalGasCap uint64) types.Message {
// Set sender address or use zero address if none specified.
var addr common.Address
if args.From != nil {
@@ -706,13 +724,16 @@ func (args *CallArgs) ToMessage(globalGasCap *big.Int) types.Message {
}
// Set default gas & gas price if none were set
- gas := uint64(math.MaxUint64 / 2)
+ gas := globalGasCap
+ if gas == 0 {
+ gas = uint64(math.MaxUint64 / 2)
+ }
if args.Gas != nil {
gas = uint64(*args.Gas)
}
- if globalGasCap != nil && globalGasCap.Uint64() < gas {
+ if globalGasCap != 0 && globalGasCap < gas {
log.Warn("Caller gas above allowance, capping", "requested", gas, "cap", globalGasCap)
- gas = globalGasCap.Uint64()
+ gas = globalGasCap
}
gasPrice := new(big.Int)
if args.GasPrice != nil {
@@ -747,7 +768,7 @@ type account struct {
StateDiff *map[common.Hash]common.Hash `json:"stateDiff"`
}
-func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap *big.Int) (*core.ExecutionResult, error) {
+func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides map[common.Address]account, vmCfg vm.Config, timeout time.Duration, globalGasCap uint64) (*core.ExecutionResult, error) {
defer func(start time.Time) { log.Debug("Executing EVM call finished", "runtime", time.Since(start)) }(time.Now())
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
@@ -817,7 +838,10 @@ func DoCall(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.Blo
if evm.Cancelled() {
return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout)
}
- return result, err
+ if err != nil {
+ return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.Gas())
+ }
+ return result, nil
}
func newRevertError(result *core.ExecutionResult) *revertError {
@@ -872,7 +896,7 @@ func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNrOr
return result.Return(), result.Err
}
-func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap *big.Int) (hexutil.Uint64, error) {
+func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash rpc.BlockNumberOrHash, gasCap uint64) (hexutil.Uint64, error) {
// Binary search the gas requirement, as it may be higher than the amount used
var (
lo uint64 = params.TxGas - 1
@@ -895,9 +919,10 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash
hi = core.CalcGasLimit(block, statedb)
}
- if gasCap != nil && hi > gasCap.Uint64() {
+ // Recap the highest gas allowance with specified gascap.
+ if gasCap != 0 && hi > gasCap {
log.Warn("Caller gas above allowance, capping", "requested", hi, "cap", gasCap)
- hi = gasCap.Uint64()
+ hi = gasCap
}
cap = hi
@@ -916,7 +941,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash
result, err := DoCall(ctx, b, args, blockNrOrHash, nil, vm.Config{}, 0, gasCap)
if err != nil {
- if err == core.ErrIntrinsicGas {
+ if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
}
return true, nil, err // Bail out
@@ -930,7 +955,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args CallArgs, blockNrOrHash
// If the error is not nil(consensus error), it means the provided message
// call or transaction will never be accepted no matter how much gas it is
- // assigened. Return the error directly, don't struggle any more.
+ // assigned. Return the error directly, don't struggle any more.
if err != nil {
return 0, err
}
@@ -1528,6 +1553,18 @@ func (args *SendTxArgs) toTransaction() *types.Transaction {
// SubmitTransaction is a helper function that submits tx to txPool and logs a message.
func SubmitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
+ // If the transaction fee cap is already specified, ensure the
+ // fee of the given transaction is _reasonable_.
+ if b.RPCTxFeeCap() != 0 {
+ currencyManager, err := NewCurrencyManager(ctx, b)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ feeCap := GetWei(b.RPCTxFeeCap())
+ if currencyManager.CmpValues(tx.Fee(), tx.FeeCurrency(), feeCap, nil) > 0 {
+ return common.Hash{}, fmt.Errorf("tx fee (%d celo) exceeds the configured cap (%d celo)", tx.Fee().Uint64(), int64(b.RPCTxFeeCap()))
+ }
+ }
if err := b.SendTx(ctx, tx); err != nil {
return common.Hash{}, err
}
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index ab5703e03d..84376aa078 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -46,7 +46,8 @@ type Backend interface {
ChainDb() ethdb.Database
AccountManager() *accounts.Manager
ExtRPCEnabled() bool
- RPCGasCap() *big.Int // global gas cap for eth_call over rpc: DoS protection
+ RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
+ RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
// Blockchain API
SetHead(number uint64)
diff --git a/internal/ethapi/util.go b/internal/ethapi/util.go
new file mode 100644
index 0000000000..07af963bc5
--- /dev/null
+++ b/internal/ethapi/util.go
@@ -0,0 +1,29 @@
+package ethapi
+
+import (
+ "context"
+ "math/big"
+
+ "github.com/celo-org/celo-blockchain/contract_comm/currency"
+ "github.com/celo-org/celo-blockchain/params"
+ "github.com/celo-org/celo-blockchain/rpc"
+)
+
+// NewCurrencyManager creates and returns a currencyManager pointing to the latest block
+// from the underlying chain from the Backend.
+func NewCurrencyManager(ctx context.Context, b Backend) (*currency.CurrencyManager, error) {
+ stateDb, header, err := b.StateAndHeaderByNumber(ctx, rpc.LatestBlockNumber)
+ if err != nil {
+ return nil, err
+ }
+ return currency.NewManager(
+ header,
+ stateDb), nil
+}
+
+// GetWei converts a celo float to a big.Int Wei representation
+func GetWei(celo float64) *big.Int {
+ floatWei := new(big.Float).Mul(big.NewFloat(params.Ether), big.NewFloat(celo))
+ wei, _ := floatWei.Int(nil)
+ return wei
+}
diff --git a/internal/utesting/utesting.go b/internal/utesting/utesting.go
new file mode 100644
index 0000000000..23c748cae9
--- /dev/null
+++ b/internal/utesting/utesting.go
@@ -0,0 +1,190 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+// Package utesting provides a standalone replacement for package testing.
+//
+// This package exists because package testing cannot easily be embedded into a
+// standalone go program. It provides an API that mirrors the standard library
+// testing API.
+package utesting
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "regexp"
+ "runtime"
+ "sync"
+ "time"
+)
+
+// Test represents a single test.
+type Test struct {
+ Name string
+ Fn func(*T)
+}
+
+// Result is the result of a test execution.
+type Result struct {
+ Name string
+ Failed bool
+ Output string
+ Duration time.Duration
+}
+
+// MatchTests returns the tests whose name matches a regular expression.
+func MatchTests(tests []Test, expr string) []Test {
+ var results []Test
+ re, err := regexp.Compile(expr)
+ if err != nil {
+ return nil
+ }
+ for _, test := range tests {
+ if re.MatchString(test.Name) {
+ results = append(results, test)
+ }
+ }
+ return results
+}
+
+// RunTests executes all given tests in order and returns their results.
+// If the report writer is non-nil, a test report is written to it in real time.
+func RunTests(tests []Test, report io.Writer) []Result {
+ results := make([]Result, len(tests))
+ for i, test := range tests {
+ start := time.Now()
+ results[i].Name = test.Name
+ results[i].Failed, results[i].Output = Run(test)
+ results[i].Duration = time.Since(start)
+ if report != nil {
+ printResult(results[i], report)
+ }
+ }
+ return results
+}
+
+func printResult(r Result, w io.Writer) {
+ pd := r.Duration.Truncate(100 * time.Microsecond)
+ if r.Failed {
+ fmt.Fprintf(w, "-- FAIL %s (%v)\n", r.Name, pd)
+ fmt.Fprintln(w, r.Output)
+ } else {
+ fmt.Fprintf(w, "-- OK %s (%v)\n", r.Name, pd)
+ }
+}
+
+// CountFailures returns the number of failed tests in the result slice.
+func CountFailures(rr []Result) int {
+ count := 0
+ for _, r := range rr {
+ if r.Failed {
+ count++
+ }
+ }
+ return count
+}
+
+// Run executes a single test.
+func Run(test Test) (bool, string) {
+ t := new(T)
+ done := make(chan struct{})
+ go func() {
+ defer close(done)
+ defer func() {
+ if err := recover(); err != nil {
+ buf := make([]byte, 4096)
+ i := runtime.Stack(buf, false)
+ t.Logf("panic: %v\n\n%s", err, buf[:i])
+ t.Fail()
+ }
+ }()
+ test.Fn(t)
+ }()
+ <-done
+ return t.failed, t.output.String()
+}
+
+// T is the value given to the test function. The test can signal failures
+// and log output by calling methods on this object.
+type T struct {
+ mu sync.Mutex
+ failed bool
+ output bytes.Buffer
+}
+
+// FailNow marks the test as having failed and stops its execution by calling
+// runtime.Goexit (which then runs all deferred calls in the current goroutine).
+func (t *T) FailNow() {
+ t.Fail()
+ runtime.Goexit()
+}
+
+// Fail marks the test as having failed but continues execution.
+func (t *T) Fail() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.failed = true
+}
+
+// Failed reports whether the test has failed.
+func (t *T) Failed() bool {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ return t.failed
+}
+
+// Log formats its arguments using default formatting, analogous to Println, and records
+// the text in the error log.
+func (t *T) Log(vs ...interface{}) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ fmt.Fprintln(&t.output, vs...)
+}
+
+// Logf formats its arguments according to the format, analogous to Printf, and records
+// the text in the error log. A final newline is added if not provided.
+func (t *T) Logf(format string, vs ...interface{}) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if len(format) == 0 || format[len(format)-1] != '\n' {
+ format += "\n"
+ }
+ fmt.Fprintf(&t.output, format, vs...)
+}
+
+// Error is equivalent to Log followed by Fail.
+func (t *T) Error(vs ...interface{}) {
+ t.Log(vs...)
+ t.Fail()
+}
+
+// Errorf is equivalent to Logf followed by Fail.
+func (t *T) Errorf(format string, vs ...interface{}) {
+ t.Logf(format, vs...)
+ t.Fail()
+}
+
+// Fatal is equivalent to Log followed by FailNow.
+func (t *T) Fatal(vs ...interface{}) {
+ t.Log(vs...)
+ t.FailNow()
+}
+
+// Fatalf is equivalent to Logf followed by FailNow.
+func (t *T) Fatalf(format string, vs ...interface{}) {
+ t.Logf(format, vs...)
+ t.FailNow()
+}
diff --git a/internal/utesting/utesting_test.go b/internal/utesting/utesting_test.go
new file mode 100644
index 0000000000..1403a5c8f7
--- /dev/null
+++ b/internal/utesting/utesting_test.go
@@ -0,0 +1,55 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package utesting
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestTest(t *testing.T) {
+ tests := []Test{
+ {
+ Name: "successful test",
+ Fn: func(t *T) {},
+ },
+ {
+ Name: "failing test",
+ Fn: func(t *T) {
+ t.Log("output")
+ t.Error("failed")
+ },
+ },
+ {
+ Name: "panicking test",
+ Fn: func(t *T) {
+ panic("oh no")
+ },
+ },
+ }
+ results := RunTests(tests, nil)
+
+ if results[0].Failed || results[0].Output != "" {
+ t.Fatalf("wrong result for successful test: %#v", results[0])
+ }
+ if !results[1].Failed || results[1].Output != "output\nfailed\n" {
+ t.Fatalf("wrong result for failing test: %#v", results[1])
+ }
+ if !results[2].Failed || !strings.HasPrefix(results[2].Output, "panic: oh no\n") {
+ t.Fatalf("wrong result for panicking test: %#v", results[2])
+ }
+}
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 36d1c59b39..7c8989e15d 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -155,7 +155,8 @@ web3._extend({
new web3._extend.Method({
name: 'accountRange',
call: 'debug_accountRange',
- params: 2
+ params: 6,
+ inputFormatter: [web3._extend.formatters.inputDefaultBlockNumberFormatter, null, null, null, null, null],
}),
new web3._extend.Method({
name: 'printBlock',
diff --git a/les/api_backend.go b/les/api_backend.go
index 322e001cbc..5fc55d9dac 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -266,10 +266,14 @@ func (b *LesApiBackend) ExtRPCEnabled() bool {
return b.extRPCEnabled
}
-func (b *LesApiBackend) RPCGasCap() *big.Int {
+func (b *LesApiBackend) RPCGasCap() uint64 {
return b.eth.config.RPCGasCap
}
+func (b *LesApiBackend) RPCTxFeeCap() float64 {
+ return b.eth.config.RPCTxFeeCap
+}
+
func (b *LesApiBackend) BloomStatus() (uint64, uint64) {
if b.eth.bloomIndexer == nil {
return 0, 0
diff --git a/les/checkpointoracle/oracle.go b/les/checkpointoracle/oracle.go
index c102718c7e..7dddf26cc6 100644
--- a/les/checkpointoracle/oracle.go
+++ b/les/checkpointoracle/oracle.go
@@ -21,7 +21,9 @@ package checkpointoracle
import (
"encoding/binary"
+ "sync"
"sync/atomic"
+ "time"
"github.com/celo-org/celo-blockchain/accounts/abi/bind"
"github.com/celo-org/celo-blockchain/common"
@@ -40,6 +42,11 @@ type CheckpointOracle struct {
running int32 // Flag whether the contract backend is set or not
getLocal func(uint64) params.TrustedCheckpoint // Function used to retrieve local checkpoint
+
+ checkMu sync.Mutex // Mutex to sync access to the fields below
+ lastCheckTime time.Time // Time we last checked the checkpoint
+ lastCheckPoint *params.TrustedCheckpoint // The last stable checkpoint
+ lastCheckPointHeight uint64 // The height of last stable checkpoint
}
// New creates a checkpoint oracle handler with given configs and callback.
@@ -88,6 +95,12 @@ func (oracle *CheckpointOracle) Contract() *checkpointoracle.CheckpointOracle {
// StableCheckpoint returns the stable checkpoint which was generated by local
// indexers and announced by trusted signers.
func (oracle *CheckpointOracle) StableCheckpoint() (*params.TrustedCheckpoint, uint64) {
+ oracle.checkMu.Lock()
+ defer oracle.checkMu.Unlock()
+ if time.Since(oracle.lastCheckTime) < 1*time.Minute {
+ return oracle.lastCheckPoint, oracle.lastCheckPointHeight
+ }
+ // Look it up properly
// Retrieve the latest checkpoint from the contract, abort if empty
latest, hash, height, err := oracle.contract.Contract().GetLatestCheckpoint(nil)
if err != nil || (latest == 0 && hash == [32]byte{}) {
@@ -103,6 +116,9 @@ func (oracle *CheckpointOracle) StableCheckpoint() (*params.TrustedCheckpoint, u
//
// In both cases, no stable checkpoint will be returned.
if local.HashEqual(hash) {
+ oracle.lastCheckTime = time.Now()
+ oracle.lastCheckPointHeight = height.Uint64()
+ oracle.lastCheckPoint = &local
return &local, height.Uint64()
}
return nil, 0
diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go
index 1bf2d670bf..82301016d6 100644
--- a/metrics/exp/exp.go
+++ b/metrics/exp/exp.go
@@ -8,6 +8,7 @@ import (
"net/http"
"sync"
+ "github.com/celo-org/celo-blockchain/log"
"github.com/celo-org/celo-blockchain/metrics"
"github.com/celo-org/celo-blockchain/metrics/prometheus"
)
@@ -52,6 +53,20 @@ func ExpHandler(r metrics.Registry) http.Handler {
return http.HandlerFunc(e.expHandler)
}
+// Setup starts a dedicated metrics server at the given address.
+// This function enables metrics reporting separate from pprof.
+func Setup(address string) {
+ m := http.NewServeMux()
+ m.Handle("/debug/metrics", ExpHandler(metrics.DefaultRegistry))
+ m.Handle("/debug/metrics/prometheus", prometheus.Handler(metrics.DefaultRegistry))
+ log.Info("Starting metrics server", "addr", fmt.Sprintf("http://%s/debug/metrics", address))
+ go func() {
+ if err := http.ListenAndServe(address, m); err != nil {
+ log.Error("Failure in running metrics server", "err", err)
+ }
+ }()
+}
+
func (exp *exp) getInt(name string) *expvar.Int {
var v *expvar.Int
exp.expvarLock.Lock()
diff --git a/mobile/geth.go b/mobile/geth.go
index 409ebf187c..4ddc073548 100644
--- a/mobile/geth.go
+++ b/mobile/geth.go
@@ -177,7 +177,7 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
}
if config.PprofAddress != "" {
- debug.StartPProf(config.PprofAddress)
+ debug.StartPProf(config.PprofAddress, true)
}
// Create the empty networking stack
diff --git a/node/config.go b/node/config.go
index 8553f7508f..1e5383caa4 100644
--- a/node/config.go
+++ b/node/config.go
@@ -103,11 +103,11 @@ type Config struct {
// a simple file name, it is placed inside the data directory (or on the root
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
// relative), then that specific path is enforced. An empty path disables IPC.
- IPCPath string `toml:",omitempty"`
+ IPCPath string
// HTTPHost is the host interface on which to start the HTTP RPC server. If this
// field is empty, no HTTP API endpoint will be started.
- HTTPHost string `toml:",omitempty"`
+ HTTPHost string
// HTTPPort is the TCP port number on which to start the HTTP RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful
@@ -131,7 +131,7 @@ type Config struct {
// HTTPModules is a list of API modules to expose via the HTTP RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
- HTTPModules []string `toml:",omitempty"`
+ HTTPModules []string
// HTTPTimeouts allows for customization of the timeout values used by the HTTP RPC
// interface.
@@ -139,7 +139,7 @@ type Config struct {
// WSHost is the host interface on which to start the websocket RPC server. If
// this field is empty, no websocket API endpoint will be started.
- WSHost string `toml:",omitempty"`
+ WSHost string
// WSPort is the TCP port number on which to start the websocket RPC server. The
// default zero value is/ valid and will pick a port number randomly (useful for
@@ -154,7 +154,7 @@ type Config struct {
// WSModules is a list of API modules to expose via the websocket RPC interface.
// If the module list is empty, all RPC API endpoints designated public will be
// exposed.
- WSModules []string `toml:",omitempty"`
+ WSModules []string
// WSExposeAll exposes all API modules via the WebSocket RPC interface rather
// than just the public ones.
@@ -165,7 +165,7 @@ type Config struct {
// GraphQLHost is the host interface on which to start the GraphQL server. If this
// field is empty, no GraphQL API endpoint will be started.
- GraphQLHost string `toml:",omitempty"`
+ GraphQLHost string
// GraphQLPort is the TCP port number on which to start the GraphQL server. The
// default zero value is/ valid and will pick a port number randomly (useful
diff --git a/p2p/peer.go b/p2p/peer.go
index e81943e8ae..430b5793ee 100644
--- a/p2p/peer.go
+++ b/p2p/peer.go
@@ -372,6 +372,7 @@ func (p *Peer) handle(msg Msg) error {
if metrics.Enabled {
m := fmt.Sprintf("%s/%s/%d/%#02x", ingressMeterName, proto.Name, proto.Version, msg.Code-proto.offset)
metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize))
+ metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1)
}
select {
case proto.in <- msg:
diff --git a/p2p/rlpx.go b/p2p/rlpx.go
index 1d8da4b86c..0314b133af 100644
--- a/p2p/rlpx.go
+++ b/p2p/rlpx.go
@@ -597,6 +597,7 @@ func (rw *rlpxFrameRW) WriteMsg(msg Msg) error {
if metrics.Enabled && msg.meterCap.Name != "" { // don't meter non-subprotocol messages
m := fmt.Sprintf("%s/%s/%d/%#02x", egressMeterName, msg.meterCap.Name, msg.meterCap.Version, msg.meterCode)
metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize))
+ metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1)
}
// write header
headbuf := make([]byte, 32)
diff --git a/rlp/encode.go b/rlp/encode.go
index 9c9e8d706d..77b591045d 100644
--- a/rlp/encode.go
+++ b/rlp/encode.go
@@ -91,13 +91,6 @@ func EncodeToReader(val interface{}) (size int, r io.Reader, err error) {
return eb.size(), &encReader{buf: eb}, nil
}
-type encbuf struct {
- str []byte // string data, contains everything except list headers
- lheads []*listhead // all list headers
- lhsize int // sum of sizes of all encoded list headers
- sizebuf []byte // 9-byte auxiliary buffer for uint encoding
-}
-
type listhead struct {
offset int // index of this header in string data
size int // total size of encoded data (including list headers)
@@ -130,19 +123,26 @@ func puthead(buf []byte, smalltag, largetag byte, size uint64) int {
return sizesize + 1
}
+type encbuf struct {
+ str []byte // string data, contains everything except list headers
+ lheads []listhead // all list headers
+ lhsize int // sum of sizes of all encoded list headers
+ sizebuf [9]byte // auxiliary buffer for uint encoding
+ bufvalue reflect.Value // used in writeByteArrayCopy
+}
+
// encbufs are pooled.
var encbufPool = sync.Pool{
- New: func() interface{} { return &encbuf{sizebuf: make([]byte, 9)} },
+ New: func() interface{} {
+ var bytes []byte
+ return &encbuf{bufvalue: reflect.ValueOf(&bytes).Elem()}
+ },
}
func (w *encbuf) reset() {
w.lhsize = 0
- if w.str != nil {
- w.str = w.str[:0]
- }
- if w.lheads != nil {
- w.lheads = w.lheads[:0]
- }
+ w.str = w.str[:0]
+ w.lheads = w.lheads[:0]
}
// encbuf implements io.Writer so it can be passed it into EncodeRLP.
@@ -164,7 +164,6 @@ func (w *encbuf) encodeStringHeader(size int) {
if size < 56 {
w.str = append(w.str, 0x80+byte(size))
} else {
- // TODO: encode to w.str directly
sizesize := putint(w.sizebuf[1:], uint64(size))
w.sizebuf[0] = 0xB7 + byte(sizesize)
w.str = append(w.str, w.sizebuf[:sizesize+1]...)
@@ -181,13 +180,29 @@ func (w *encbuf) encodeString(b []byte) {
}
}
-func (w *encbuf) list() *listhead {
- lh := &listhead{offset: len(w.str), size: w.lhsize}
- w.lheads = append(w.lheads, lh)
- return lh
+func (w *encbuf) encodeUint(i uint64) {
+ if i == 0 {
+ w.str = append(w.str, 0x80)
+ } else if i < 128 {
+ // fits single byte
+ w.str = append(w.str, byte(i))
+ } else {
+ s := putint(w.sizebuf[1:], i)
+ w.sizebuf[0] = 0x80 + byte(s)
+ w.str = append(w.str, w.sizebuf[:s+1]...)
+ }
}
-func (w *encbuf) listEnd(lh *listhead) {
+// list adds a new list header to the header stack. It returns the index
+// of the header. The caller must call listEnd with this index after encoding
+// the content of the list.
+func (w *encbuf) list() int {
+ w.lheads = append(w.lheads, listhead{offset: len(w.str), size: w.lhsize})
+ return len(w.lheads) - 1
+}
+
+func (w *encbuf) listEnd(index int) {
+ lh := &w.lheads[index]
lh.size = w.size() - lh.offset - lh.size
if lh.size < 56 {
w.lhsize++ // length encoded into kind tag
@@ -230,7 +245,7 @@ func (w *encbuf) toWriter(out io.Writer) (err error) {
}
}
// write the header
- enc := head.encode(w.sizebuf)
+ enc := head.encode(w.sizebuf[:])
if _, err = out.Write(enc); err != nil {
return err
}
@@ -296,7 +311,7 @@ func (r *encReader) next() []byte {
return p
}
r.lhpos++
- return head.encode(r.buf.sizebuf)
+ return head.encode(r.buf.sizebuf[:])
case r.strpos < len(r.buf.str):
// String data at the end, after all list headers.
@@ -309,10 +324,7 @@ func (r *encReader) next() []byte {
}
}
-var (
- encoderInterface = reflect.TypeOf(new(Encoder)).Elem()
- big0 = big.NewInt(0)
-)
+var encoderInterface = reflect.TypeOf(new(Encoder)).Elem()
// makeWriter creates a writer function for the given type.
func makeWriter(typ reflect.Type, ts tags) (writer, error) {
@@ -337,7 +349,7 @@ func makeWriter(typ reflect.Type, ts tags) (writer, error) {
case kind == reflect.Slice && isByte(typ.Elem()):
return writeBytes, nil
case kind == reflect.Array && isByte(typ.Elem()):
- return writeByteArray, nil
+ return makeByteArrayWriter(typ), nil
case kind == reflect.Slice || kind == reflect.Array:
return makeSliceWriter(typ, ts)
case kind == reflect.Struct:
@@ -349,28 +361,13 @@ func makeWriter(typ reflect.Type, ts tags) (writer, error) {
}
}
-func isByte(typ reflect.Type) bool {
- return typ.Kind() == reflect.Uint8 && !typ.Implements(encoderInterface)
-}
-
func writeRawValue(val reflect.Value, w *encbuf) error {
w.str = append(w.str, val.Bytes()...)
return nil
}
func writeUint(val reflect.Value, w *encbuf) error {
- i := val.Uint()
- if i == 0 {
- w.str = append(w.str, 0x80)
- } else if i < 128 {
- // fits single byte
- w.str = append(w.str, byte(i))
- } else {
- // TODO: encode int to w.str directly
- s := putint(w.sizebuf[1:], i)
- w.sizebuf[0] = 0x80 + byte(s)
- w.str = append(w.str, w.sizebuf[:s+1]...)
- }
+ w.encodeUint(val.Uint())
return nil
}
@@ -397,13 +394,32 @@ func writeBigIntNoPtr(val reflect.Value, w *encbuf) error {
return writeBigInt(&i, w)
}
+// wordBytes is the number of bytes in a big.Word
+const wordBytes = (32 << (uint64(^big.Word(0)) >> 63)) / 8
+
func writeBigInt(i *big.Int, w *encbuf) error {
- if cmp := i.Cmp(big0); cmp == -1 {
+ if i.Sign() == -1 {
return fmt.Errorf("rlp: cannot encode negative *big.Int")
- } else if cmp == 0 {
- w.str = append(w.str, 0x80)
- } else {
- w.encodeString(i.Bytes())
+ }
+ bitlen := i.BitLen()
+ if bitlen <= 64 {
+ w.encodeUint(i.Uint64())
+ return nil
+ }
+ // Integer is larger than 64 bits, encode from i.Bits().
+ // The minimal byte length is bitlen rounded up to the next
+ // multiple of 8, divided by 8.
+ length := ((bitlen + 7) & -8) >> 3
+ w.encodeStringHeader(length)
+ w.str = append(w.str, make([]byte, length)...)
+ index := length
+ buf := w.str[len(w.str)-length:]
+ for _, d := range i.Bits() {
+ for j := 0; j < wordBytes && index > 0; j++ {
+ index--
+ buf[index] = byte(d)
+ d >>= 8
+ }
}
return nil
}
@@ -413,7 +429,52 @@ func writeBytes(val reflect.Value, w *encbuf) error {
return nil
}
-func writeByteArray(val reflect.Value, w *encbuf) error {
+var byteType = reflect.TypeOf(byte(0))
+
+func makeByteArrayWriter(typ reflect.Type) writer {
+ length := typ.Len()
+ if length == 0 {
+ return writeLengthZeroByteArray
+ } else if length == 1 {
+ return writeLengthOneByteArray
+ }
+ if typ.Elem() != byteType {
+ return writeNamedByteArray
+ }
+ return func(val reflect.Value, w *encbuf) error {
+ writeByteArrayCopy(length, val, w)
+ return nil
+ }
+}
+
+func writeLengthZeroByteArray(val reflect.Value, w *encbuf) error {
+ w.str = append(w.str, 0x80)
+ return nil
+}
+
+func writeLengthOneByteArray(val reflect.Value, w *encbuf) error {
+ b := byte(val.Index(0).Uint())
+ if b <= 0x7f {
+ w.str = append(w.str, b)
+ } else {
+ w.str = append(w.str, 0x81, b)
+ }
+ return nil
+}
+
+// writeByteArrayCopy encodes byte arrays using reflect.Copy. This is
+// the fast path for [N]byte where N > 1.
+func writeByteArrayCopy(length int, val reflect.Value, w *encbuf) {
+ w.encodeStringHeader(length)
+ offset := len(w.str)
+ w.str = append(w.str, make([]byte, length)...)
+ w.bufvalue.SetBytes(w.str[offset:])
+ reflect.Copy(w.bufvalue, val)
+}
+
+// writeNamedByteArray encodes byte arrays with named element type.
+// This exists because reflect.Copy can't be used with such types.
+func writeNamedByteArray(val reflect.Value, w *encbuf) error {
if !val.CanAddr() {
// Slice requires the value to be addressable.
// Make it addressable by copying.
diff --git a/rlp/encode_test.go b/rlp/encode_test.go
index b4b9e51287..67b3c05422 100644
--- a/rlp/encode_test.go
+++ b/rlp/encode_test.go
@@ -25,6 +25,8 @@ import (
"math/big"
"sync"
"testing"
+
+ "github.com/celo-org/celo-blockchain/common/math"
)
type testEncoder struct {
@@ -137,16 +139,43 @@ var encTests = []encTest{
// negative ints are not supported
{val: big.NewInt(-1), error: "rlp: cannot encode negative *big.Int"},
- // byte slices, strings
+ // byte arrays
+ {val: [0]byte{}, output: "80"},
+ {val: [1]byte{0}, output: "00"},
+ {val: [1]byte{1}, output: "01"},
+ {val: [1]byte{0x7F}, output: "7F"},
+ {val: [1]byte{0x80}, output: "8180"},
+ {val: [1]byte{0xFF}, output: "81FF"},
+ {val: [3]byte{1, 2, 3}, output: "83010203"},
+ {val: [57]byte{1, 2, 3}, output: "B839010203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
+
+ // named byte type arrays
+ {val: [0]namedByteType{}, output: "80"},
+ {val: [1]namedByteType{0}, output: "00"},
+ {val: [1]namedByteType{1}, output: "01"},
+ {val: [1]namedByteType{0x7F}, output: "7F"},
+ {val: [1]namedByteType{0x80}, output: "8180"},
+ {val: [1]namedByteType{0xFF}, output: "81FF"},
+ {val: [3]namedByteType{1, 2, 3}, output: "83010203"},
+ {val: [57]namedByteType{1, 2, 3}, output: "B839010203000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},
+
+ // byte slices
{val: []byte{}, output: "80"},
+ {val: []byte{0}, output: "00"},
{val: []byte{0x7E}, output: "7E"},
{val: []byte{0x7F}, output: "7F"},
{val: []byte{0x80}, output: "8180"},
{val: []byte{1, 2, 3}, output: "83010203"},
+ // named byte type slices
+ {val: []namedByteType{}, output: "80"},
+ {val: []namedByteType{0}, output: "00"},
+ {val: []namedByteType{0x7E}, output: "7E"},
+ {val: []namedByteType{0x7F}, output: "7F"},
+ {val: []namedByteType{0x80}, output: "8180"},
{val: []namedByteType{1, 2, 3}, output: "83010203"},
- {val: [...]namedByteType{1, 2, 3}, output: "83010203"},
+ // strings
{val: "", output: "80"},
{val: "\x7E", output: "7E"},
{val: "\x7F", output: "7F"},
@@ -401,3 +430,36 @@ func TestEncodeToReaderReturnToPool(t *testing.T) {
}
wg.Wait()
}
+
+var sink interface{}
+
+func BenchmarkIntsize(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sink = intsize(0x12345678)
+ }
+}
+
+func BenchmarkPutint(b *testing.B) {
+ buf := make([]byte, 8)
+ for i := 0; i < b.N; i++ {
+ putint(buf, 0x12345678)
+ sink = buf
+ }
+}
+
+func BenchmarkEncodeBigInts(b *testing.B) {
+ ints := make([]*big.Int, 200)
+ for i := range ints {
+ ints[i] = math.BigPow(2, int64(i))
+ }
+ out := bytes.NewBuffer(make([]byte, 0, 4096))
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ out.Reset()
+ if err := Encode(out, ints); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/rlp/typecache.go b/rlp/typecache.go
index e9a1e3f9e2..6026e1a649 100644
--- a/rlp/typecache.go
+++ b/rlp/typecache.go
@@ -210,6 +210,10 @@ func isUint(k reflect.Kind) bool {
return k >= reflect.Uint && k <= reflect.Uintptr
}
+func isByte(typ reflect.Type) bool {
+ return typ.Kind() == reflect.Uint8 && !typ.Implements(encoderInterface)
+}
+
func isByteArray(typ reflect.Type) bool {
return (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array) && isByte(typ.Elem())
}
diff --git a/tests/init.go b/tests/init.go
index 145b5ae984..88f7908450 100644
--- a/tests/init.go
+++ b/tests/init.go
@@ -19,6 +19,7 @@ package tests
import (
"fmt"
"math/big"
+ "sort"
"github.com/celo-org/celo-blockchain/params"
)
@@ -142,6 +143,16 @@ var Forks = map[string]*params.ChainConfig{
},
}
+// Returns the set of defined fork names
+func AvailableForks() []string {
+ var availableForks []string
+ for k := range Forks {
+ availableForks = append(availableForks, k)
+ }
+ sort.Strings(availableForks)
+ return availableForks
+}
+
// UnsupportedForkError is returned when a test requests a fork that isn't implemented.
type UnsupportedForkError struct {
Name string
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 4abb8dc72e..89ccc7ccab 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -110,11 +110,11 @@ type stTransactionMarshaling struct {
PrivateKey hexutil.Bytes
}
-// getVMConfig takes a fork definition and returns a chain config.
+// GetChainConfig takes a fork definition and returns a chain config.
// The fork definition can be
// - a plain forkname, e.g. `Byzantium`,
// - a fork basename, and a list of EIPs to enable; e.g. `Byzantium+1884+1283`.
-func getVMConfig(forkString string) (baseConfig *params.ChainConfig, eips []int, err error) {
+func GetChainConfig(forkString string) (baseConfig *params.ChainConfig, eips []int, err error) {
var (
splitForks = strings.Split(forkString, "+")
ok bool
@@ -127,6 +127,9 @@ func getVMConfig(forkString string) (baseConfig *params.ChainConfig, eips []int,
if eipNum, err := strconv.Atoi(eip); err != nil {
return nil, nil, fmt.Errorf("syntax error, invalid eip number %v", eipNum)
} else {
+ if !vm.ValidEip(eipNum) {
+ return nil, nil, fmt.Errorf("syntax error, invalid eip number %v", eipNum)
+ }
eips = append(eips, eipNum)
}
}
@@ -164,7 +167,7 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo
// RunNoVerify runs a specific subtest and returns the statedb and post-state root
func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool) (*snapshot.Tree, *state.StateDB, common.Hash, error) {
- config, eips, err := getVMConfig(subtest.Fork)
+ config, eips, err := GetChainConfig(subtest.Fork)
if err != nil {
return nil, nil, common.Hash{}, UnsupportedForkError{subtest.Fork}
}
diff --git a/trie/committer.go b/trie/committer.go
index cb0e883e72..2bead3fdbc 100644
--- a/trie/committer.go
+++ b/trie/committer.go
@@ -22,6 +22,7 @@ import (
"sync"
"github.com/celo-org/celo-blockchain/common"
+ "github.com/celo-org/celo-blockchain/crypto"
"github.com/celo-org/celo-blockchain/rlp"
"golang.org/x/crypto/sha3"
)
@@ -46,7 +47,7 @@ type leaf struct {
// processed sequentially - onleaf will never be called in parallel or out of order.
type committer struct {
tmp sliceBuffer
- sha keccakState
+ sha crypto.KeccakState
onleaf LeafCallback
leafCh chan *leaf
@@ -57,7 +58,7 @@ var committerPool = sync.Pool{
New: func() interface{} {
return &committer{
tmp: make(sliceBuffer, 0, 550), // cap is as large as a full fullNode.
- sha: sha3.NewLegacyKeccak256().(keccakState),
+ sha: sha3.NewLegacyKeccak256().(crypto.KeccakState),
}
},
}
diff --git a/trie/database.go b/trie/database.go
index 10cb485581..8e316115f4 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -349,14 +349,15 @@ func (db *Database) insert(hash common.Hash, size int, node node) {
}
// insertPreimage writes a new trie node pre-image to the memory database if it's
-// yet unknown. The method will make a copy of the slice.
+// yet unknown. The method will NOT make a copy of the slice,
+// only use if the preimage will NOT be changed later on.
//
// Note, this method assumes that the database's lock is held!
func (db *Database) insertPreimage(hash common.Hash, preimage []byte) {
if _, ok := db.preimages[hash]; ok {
return
}
- db.preimages[hash] = common.CopyBytes(preimage)
+ db.preimages[hash] = preimage
db.preimagesSize += common.StorageSize(common.HashLength + len(preimage))
}
diff --git a/trie/hasher.go b/trie/hasher.go
index 50a6ed94e6..12c13d2cbf 100644
--- a/trie/hasher.go
+++ b/trie/hasher.go
@@ -17,21 +17,13 @@
package trie
import (
- "hash"
"sync"
+ "github.com/celo-org/celo-blockchain/crypto"
"github.com/celo-org/celo-blockchain/rlp"
"golang.org/x/crypto/sha3"
)
-// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
-// Read to get a variable amount of data from the hash state. Read is faster than Sum
-// because it doesn't copy the internal state, but also modifies the internal state.
-type keccakState interface {
- hash.Hash
- Read([]byte) (int, error)
-}
-
type sliceBuffer []byte
func (b *sliceBuffer) Write(data []byte) (n int, err error) {
@@ -46,7 +38,7 @@ func (b *sliceBuffer) Reset() {
// hasher is a type used for the trie Hash operation. A hasher has some
// internal preallocated temp space
type hasher struct {
- sha keccakState
+ sha crypto.KeccakState
tmp sliceBuffer
parallel bool // Whether to use paralallel threads when hashing
}
@@ -56,7 +48,7 @@ var hasherPool = sync.Pool{
New: func() interface{} {
return &hasher{
tmp: make(sliceBuffer, 0, 550), // cap is as large as a full fullNode.
- sha: sha3.NewLegacyKeccak256().(keccakState),
+ sha: sha3.NewLegacyKeccak256().(crypto.KeccakState),
}
},
}
diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go
index 06506fd5a6..9a2ae99991 100644
--- a/whisper/whisperv6/whisper.go
+++ b/whisper/whisperv6/whisper.go
@@ -249,7 +249,10 @@ func (whisper *Whisper) SetBloomFilter(bloom []byte) error {
go func() {
// allow some time before all the peers have processed the notification
defer whisper.wg.Done()
- time.Sleep(time.Duration(whisper.syncAllowance) * time.Second)
+ ticker := time.NewTicker(time.Duration(whisper.syncAllowance) * time.Second)
+ defer ticker.Stop()
+
+ <-ticker.C
whisper.settings.Store(bloomFilterToleranceIdx, b)
}()
@@ -269,7 +272,10 @@ func (whisper *Whisper) SetMinimumPoW(val float64) error {
go func() {
defer whisper.wg.Done()
// allow some time before all the peers have processed the notification
- time.Sleep(time.Duration(whisper.syncAllowance) * time.Second)
+ ticker := time.NewTicker(time.Duration(whisper.syncAllowance) * time.Second)
+ defer ticker.Stop()
+
+ <-ticker.C
whisper.settings.Store(minPowToleranceIdx, val)
}()
diff --git a/whisper/whisperv6/whisper_test.go b/whisper/whisperv6/whisper_test.go
index c87bdbcc09..ca4b12bee1 100644
--- a/whisper/whisperv6/whisper_test.go
+++ b/whisper/whisperv6/whisper_test.go
@@ -489,8 +489,10 @@ func TestExpiry(t *testing.T) {
// wait till received or timeout
var received, expired bool
+ ticker := time.NewTicker(100 * time.Millisecond)
+ defer ticker.Stop()
for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
+ <-ticker.C
if len(w.Envelopes()) == messagesCount {
received = true
break
@@ -503,7 +505,7 @@ func TestExpiry(t *testing.T) {
// wait till expired or timeout
for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
+ <-ticker.C
if len(w.Envelopes()) == 0 {
expired = true
break
@@ -582,8 +584,10 @@ func TestCustomization(t *testing.T) {
// wait till received or timeout
var received bool
+ ticker := time.NewTicker(100 * time.Millisecond)
+ defer ticker.Stop()
for j := 0; j < 20; j++ {
- time.Sleep(100 * time.Millisecond)
+ <-ticker.C
if len(w.Envelopes()) > 1 {
received = true
break
@@ -599,7 +603,7 @@ func TestCustomization(t *testing.T) {
if err != nil {
t.Fatalf("failed subscribe with seed %d: %s.", seed, err)
}
- time.Sleep(5 * time.Millisecond)
+ <-ticker.C
mail := f.Retrieve()
if len(mail) > 0 {
t.Fatalf("received premature mail")
@@ -670,8 +674,10 @@ func TestSymmetricSendCycle(t *testing.T) {
// wait till received or timeout
var received bool
+ ticker := time.NewTicker(10 * time.Millisecond)
+ defer ticker.Stop()
for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
+ <-ticker.C
if len(w.Envelopes()) > 0 {
received = true
break
@@ -683,7 +689,7 @@ func TestSymmetricSendCycle(t *testing.T) {
}
// check w.messages()
- time.Sleep(5 * time.Millisecond)
+ <-ticker.C
mail1 := filter1.Retrieve()
mail2 := filter2.Retrieve()
if len(mail2) == 0 {
@@ -743,8 +749,10 @@ func TestSymmetricSendWithoutAKey(t *testing.T) {
// wait till received or timeout
var received bool
+ ticker := time.NewTicker(10 * time.Millisecond)
+ defer ticker.Stop()
for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
+ <-ticker.C
if len(w.Envelopes()) > 0 {
received = true
break
@@ -756,7 +764,7 @@ func TestSymmetricSendWithoutAKey(t *testing.T) {
}
// check w.messages()
- time.Sleep(5 * time.Millisecond)
+ <-ticker.C
mail := filter.Retrieve()
if len(mail) == 0 {
t.Fatalf("did not receive message in spite of not setting a public key")
@@ -809,8 +817,10 @@ func TestSymmetricSendKeyMismatch(t *testing.T) {
// wait till received or timeout
var received bool
+ ticker := time.NewTicker(10 * time.Millisecond)
+ defer ticker.Stop()
for j := 0; j < 200; j++ {
- time.Sleep(10 * time.Millisecond)
+ <-ticker.C
if len(w.Envelopes()) > 0 {
received = true
break
@@ -822,7 +832,7 @@ func TestSymmetricSendKeyMismatch(t *testing.T) {
}
// check w.messages()
- time.Sleep(5 * time.Millisecond)
+ <-ticker.C
mail := filter.Retrieve()
if len(mail) > 0 {
t.Fatalf("received a message when keys weren't matching")