diff --git a/BUILDING.md b/BUILDING.md index a9e9d8162..57a48572c 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,6 +1,8 @@ # Building Quorum +Note: Building Quorum requires both a Go (version 1.9 or later) and a C compiler. You can install them using your favourite package manager. + Clone the repository and build the source: ``` diff --git a/HACKING.md b/NOTES.md similarity index 50% rename from HACKING.md rename to NOTES.md index e408d464f..e38ff0296 100644 --- a/HACKING.md +++ b/NOTES.md @@ -2,7 +2,57 @@ ## How does private state work? -Let's look at the EVM structure: +Original commit from Jeff explains the dual public and private state with INITIAL restrictions: +``` +commit 763f939f4725daa136161868d3b01fa7a84eb71e +Author: Jeffrey Wilcke +Date: Mon Oct 31 12:46:40 2016 +0100 + + core, core/vm: dual state & read only EVM + + This commit implements a dual state approach. The dual state approach + separates public and private state by making the core vm environment + context aware. + + Although not currently implemented it will need to prohibit value + transfers and it must initialise all transactions from accounts on the + public state. This means that sending transactions increments the + account nonce on the public state and contract addresses are derived + from the public state when initialised by a transaction. For obvious + reasons, contract created by private contracts are still derived from + public state. + + This is required in order to have consensus over the public state at all + times as non-private participants would still process the transaction on + the public state even though private payload can not be decrypted. This + means that participants of a private group must do the same in order to + have public consensus. However the creation of the contract and + interaction still occurs on the private state. + + It implements support for the following calling model: + + S: sender, (X): private, X: public, ->: direction, [ ]: read only mode + + 1. S -> A -> B + 2. S -> (A) -> (B) + 3. S -> (A) -> [ B -> C ] + + It does not support + + 1. (S) -> A + 2. (S) -> (A) + 3. S -> (A) -> B + + Implemented "read only" mode for the EVM. Read only mode is checked + during any opcode that could potentially modify the state. If such an + opcode is encountered during "read only", it throws an exception. + + The EVM is flagged "read only" when a private contract calls in to + public state. +``` + + +Some things have changed since, let's look at the EVM structure in some more detail: ```go type EVM struct { diff --git a/README.md b/README.md index 0e7515c39..5fa8f8612 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ The official docker containers can be found under https://hub.docker.com/u/quoru * [Constellation](https://github.com/jpmorganchase/constellation): Haskell implementation of peer-to-peer encrypted message exchange for transaction privacy * [Tessera](https://github.com/jpmorganchase/tessera): Java implementation of peer-to-peer encrypted message exchange for transaction privacy * Quorum supported consensuses - * [Raft Consensus Documentation](raft/doc.md) - * [Istanbul BFT Consensus Documentation](https://github.com/ethereum/EIPs/issues/650): [RPC API](https://github.com/getamis/go-ethereum/wiki/RPC-API) and [technical article](https://medium.com/getamis/istanbul-bft-ibft-c2758b7fe6ff) + * [Raft Consensus Documentation](docs/raft.md) + * [Istanbul BFT Consensus Documentation](https://github.com/ethereum/EIPs/issues/650): [RPC API](https://github.com/jpmorganchase/quorum/blob/master/docs/istanbul-rpc-api.md) and [technical article](https://medium.com/getamis/istanbul-bft-ibft-c2758b7fe6ff). Please note that updated istanbul-tools is now hosted in [this](https://github.com/jpmorganchase/istanbul-tools/) repository * [Clique POA Consensus Documentation](https://github.com/ethereum/EIPs/issues/225) and a [guide to setup clique json](https://modalduality.org/posts/puppeth/) with [puppeth](https://blog.ethereum.org/2017/04/14/geth-1-6-puppeth-master/) * [ZSL](https://github.com/jpmorganchase/quorum/wiki/ZSL) wiki page and [documentation](https://github.com/jpmorganchase/zsl-q/blob/master/README.md) * [quorum-tools](https://github.com/jpmorganchase/quorum-tools): local cluster orchestration, and integration testing tool diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 349a3a21a..9a9bf7793 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -820,17 +820,12 @@ func setIPC(ctx *cli.Context, cfg *node.Config) { // makeDatabaseHandles raises out the number of allowed file handles per process // for Geth and returns half of the allowance to assign to the database. func makeDatabaseHandles() int { - limit, err := fdlimit.Current() + limit, err := fdlimit.Maximum() if err != nil { Fatalf("Failed to retrieve file descriptor allowance: %v", err) } - if limit < 2048 { - if err := fdlimit.Raise(2048); err != nil { - Fatalf("Failed to raise file descriptor allowance: %v", err) - } - } - if limit > 2048 { // cap database file descriptors even if more is available - limit = 2048 + if err := fdlimit.Raise(uint64(limit)); err != nil { + Fatalf("Failed to raise file descriptor allowance: %v", err) } return limit / 2 // Leave half for networking and other stuff } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 28950e749..76a865635 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -31,7 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" - set "gopkg.in/fatih/set.v0" + "gopkg.in/fatih/set.v0" ) // Ethash proof-of-work protocol constants. @@ -285,9 +285,9 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent * if diff < 0 { diff *= -1 } - limit := parent.GasLimit / params.GasLimitBoundDivisor + limit := parent.GasLimit / params.OriginalGasLimitBoundDivisor - if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit { + if uint64(diff) >= limit || header.GasLimit < params.OriginnalMinGasLimit { return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit) } // Verify that the block number is parent's +1 diff --git a/core/dual_state_test.go b/core/dual_state_test.go index b091ff04c..4af00ab67 100644 --- a/core/dual_state_test.go +++ b/core/dual_state_test.go @@ -119,3 +119,95 @@ func TestDualStateReadOnly(t *testing.T) { t.Errorf("expected 0 got %x", value) } } + +var ( + calleeAddress = common.Address{2} + calleeContractCode = "600a6000526001601ff300" // a function that returns 10 + callerAddress = common.Address{1} + // a functionn that calls the callee's function at its address and return the same value + //000000: PUSH1 0x01 + //000002: PUSH1 0x00 + //000004: PUSH1 0x00 + //000006: PUSH1 0x00 + //000008: PUSH20 0x0200000000000000000000000000000000000000 + //000029: PUSH3 0x0186a0 + //000033: STATICCALL + //000034: PUSH1 0x01 + //000036: PUSH1 0x00 + //000038: RETURN + //000039: STOP + callerContractCode = "6001600060006000730200000000000000000000000000000000000000620186a0fa60016000f300" +) + +func verifyStaticCall(t *testing.T, privateState *state.StateDB, publicState *state.StateDB, expectedHash common.Hash) { + author := common.Address{} + msg := callmsg{ + addr: author, + to: &callerAddress, + value: big.NewInt(1), + gas: 1000000, + gasPrice: new(big.Int), + data: nil, + } + + ctx := NewEVMContext(msg, &dualStateTestHeader, nil, &author) + env := vm.NewEVM(ctx, publicState, privateState, ¶ms.ChainConfig{ + ByzantiumBlock: new(big.Int), + }, vm.Config{}) + + ret, _, err := env.Call(vm.AccountRef(author), callerAddress, msg.data, msg.gas, new(big.Int)) + + if err != nil { + t.Fatalf("Call error: %s", err) + } + value := common.Hash{ret[0]} + if value != expectedHash { + t.Errorf("expected %x got %x", expectedHash, value) + } +} + +func TestStaticCall_whenPublicToPublic(t *testing.T) { + db := ethdb.NewMemDatabase() + + publicState, _ := state.New(common.Hash{}, state.NewDatabase(db)) + publicState.SetCode(callerAddress, common.Hex2Bytes(callerContractCode)) + publicState.SetCode(calleeAddress, common.Hex2Bytes(calleeContractCode)) + + verifyStaticCall(t, publicState, publicState, common.Hash{10}) +} + +func TestStaticCall_whenPublicToPrivateInTheParty(t *testing.T) { + db := ethdb.NewMemDatabase() + + privateState, _ := state.New(common.Hash{}, state.NewDatabase(db)) + privateState.SetCode(calleeAddress, common.Hex2Bytes(calleeContractCode)) + + publicState, _ := state.New(common.Hash{}, state.NewDatabase(db)) + publicState.SetCode(callerAddress, common.Hex2Bytes(callerContractCode)) + + verifyStaticCall(t, privateState, publicState, common.Hash{10}) +} + +func TestStaticCall_whenPublicToPrivateNotInTheParty(t *testing.T) { + + db := ethdb.NewMemDatabase() + + privateState, _ := state.New(common.Hash{}, state.NewDatabase(db)) + + publicState, _ := state.New(common.Hash{}, state.NewDatabase(db)) + publicState.SetCode(callerAddress, common.Hex2Bytes(callerContractCode)) + + verifyStaticCall(t, privateState, publicState, common.Hash{0}) +} + +func TestStaticCall_whenPrivateToPublic(t *testing.T) { + db := ethdb.NewMemDatabase() + + privateState, _ := state.New(common.Hash{}, state.NewDatabase(db)) + privateState.SetCode(callerAddress, common.Hex2Bytes(callerContractCode)) + + publicState, _ := state.New(common.Hash{}, state.NewDatabase(db)) + publicState.SetCode(calleeAddress, common.Hex2Bytes(calleeContractCode)) + + verifyStaticCall(t, privateState, publicState, common.Hash{10}) +} diff --git a/core/tx_pool.go b/core/tx_pool.go index b96d9e8d9..1a23f69b4 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -579,7 +579,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return ErrInvalidGasPrice } // Heuristic limit, reject transactions over 32KB to prevent DOS attacks - if tx.Size() > 32*1024 { + // UPDATED to 64KB to support the deployment of bigger contract due to the pressing need for sophisticated/complex contract in financial/capital markets - Nathan Aw + if tx.Size() > 64*1024 { return ErrOversizedData } // Transactions can't be negative. This may never happen using RLP decoded diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 1858abeb0..5d085f2f8 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -278,8 +278,8 @@ func TestInvalidTransactions(t *testing.T) { t.Error("expected", ErrGasLimit, "; got", err) } - data := make([]byte, (32*1024)+1) - tx2, _ := types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(100),100000, big.NewInt(1), data), types.HomesteadSigner{}, key) + data := make([]byte, (64*1024)+1) + tx2, _ := types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), data), types.HomesteadSigner{}, key) if err := pool.AddRemote(tx2); err != ErrOversizedData { t.Error("expected", ErrOversizedData, "; got", err) } diff --git a/core/types/bloom9.go b/core/types/bloom9.go index a76b6f33c..0a028ae1d 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -63,6 +63,14 @@ func (b *Bloom) Add(d *big.Int) { b.SetBytes(bin.Bytes()) } +// OrBloom executes an Or operation on the bloom +func (b *Bloom) OrBloom(bl []byte) { + bin := new(big.Int).SetBytes(b[:]) + input := new(big.Int).SetBytes(bl[:]) + bin.Or(bin, input) + b.SetBytes(bin.Bytes()) +} + // Big converts b to a big integer. func (b Bloom) Big() *big.Int { return new(big.Int).SetBytes(b[:]) diff --git a/core/types/transaction.go b/core/types/transaction.go index 5af5fef77..a3cd3de45 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -493,6 +493,9 @@ func (tx *Transaction) IsPrivate() bool { } func (tx *Transaction) SetPrivate() { + if tx.IsPrivate() { + return + } if tx.data.V.Int64() == 28 { tx.data.V.SetUint64(38) } else { diff --git a/core/vm/evm.go b/core/vm/evm.go index 55826102c..6eef7fef8 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -334,6 +334,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte if evm.vmConfig.NoRecursion && evm.depth > 0 { return nil, gas, nil } + // Fail if we're trying to execute above the call depth limit if evm.depth > int(params.CallCreateDepth) { return nil, gas, ErrDepth @@ -348,20 +349,21 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte var ( to = AccountRef(addr) - snapshot = evm.StateDB.Snapshot() + stateDb = getDualState(evm, addr) + snapshot = stateDb.Snapshot() ) // Initialise a new contract and set the code that is to be used by the // EVM. The contract is a scoped environment for this execution context // only. contract := NewContract(caller, to, new(big.Int), gas) - contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr)) + contract.SetCallCode(&addr, stateDb.GetCodeHash(addr), stateDb.GetCode(addr)) // 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 // when we're in Homestead this also counts for code storage gas errors. ret, err = run(evm, contract, input) if err != nil { - evm.StateDB.RevertToSnapshot(snapshot) + stateDb.RevertToSnapshot(snapshot) if err != errExecutionReverted { contract.UseGas(contract.Gas) } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1ec13ba35..ab779ee53 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -391,7 +391,10 @@ func opAddress(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack * func opBalance(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - slot.Set(evm.StateDB.GetBalance(common.BigToAddress(slot))) + addr := common.BigToAddress(slot) + // Quorum: get public/private state db based on addr + balance := getDualState(evm, addr).GetBalance(addr) + slot.Set(balance) return nil, nil } @@ -457,7 +460,9 @@ func opReturnDataCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, func opExtCodeSize(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { slot := stack.peek() - slot.SetUint64(uint64(evm.StateDB.GetCodeSize(common.BigToAddress(slot)))) + addr := common.BigToAddress(slot) + // Quorum: get public/private state db based on addr + slot.SetUint64(uint64(getDualState(evm, addr).GetCodeSize(addr))) return nil, nil } @@ -489,7 +494,8 @@ func opExtCodeCopy(pc *uint64, evm *EVM, contract *Contract, memory *Memory, sta codeOffset = stack.pop() length = stack.pop() ) - codeCopy := getDataBig(evm.StateDB.GetCode(addr), codeOffset, length) + // Quorum: get public/private state db based on addr + codeCopy := getDataBig(getDualState(evm, addr).GetCode(addr), codeOffset, length) memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) evm.interpreter.intPool.put(memOffset, codeOffset, length) @@ -571,7 +577,8 @@ func opMstore8(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack * func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := stack.peek() - val := evm.StateDB.GetState(contract.Address(), common.BigToHash(loc)) + // Quorum: get public/private state db based on addr + val := getDualState(evm, contract.Address()).GetState(contract.Address(), common.BigToHash(loc)) loc.SetBytes(val.Bytes()) return nil, nil } @@ -579,7 +586,8 @@ func opSload(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *St func opSstore(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := common.BigToHash(stack.pop()) val := stack.pop() - evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val)) + // Quorum: get public/private state db based on addr + getDualState(evm, contract.Address()).SetState(contract.Address(), loc, common.BigToHash(val)) evm.interpreter.intPool.put(val) return nil, nil @@ -794,10 +802,12 @@ func opStop(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Sta } func opSuicide(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - balance := evm.StateDB.GetBalance(contract.Address()) - evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance) + // Quorum: get public/private state db based on addr + db := getDualState(evm, contract.Address()) + balance := db.GetBalance(contract.Address()) + db.AddBalance(common.BigToAddress(stack.pop()), balance) - evm.StateDB.Suicide(contract.Address()) + db.Suicide(contract.Address()) return nil, nil } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 4b8d80caa..be6b1773d 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -173,7 +173,6 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er // Get the operation from the jump table and validate the stack to ensure there are // enough stack items available to perform the operation. - op = contract.GetOp(pc) operation := in.cfg.JumpTable[op] if !operation.valid { return nil, fmt.Errorf("invalid opcode 0x%x", int(op)) diff --git a/docs/README.md b/docs/README.md index cbddd4a82..dd50a04a7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -3,6 +3,8 @@ * [Whitepaper](./Quorum%20Whitepaper%20v0.2.pdf) (PDF) - Quorum Whitepaper [demo video](https://vimeo.com/user5833792/review/210456842/a42d0fcb87) * [Design](./design.md) - Quorum design overview + * [Raft Specific Documentation](./raft.md) - Overview of raft implementation + * [Istanbul RPC API](./istanbul-rpc-api.md) - Overview of Istanbul BFT APIs * [Privacy](./privacy.md) - Sending private transactions [privacy video](https://vimeo.com/user5833792/review/210456729/8f70cfaaa5) * [Running](./running.md) - Detailed instructions for running Quorum nodes (see also [Constellation](https://github.com/jpmorganchase/constellation), [Tessera](https://github.com/jpmorganchase/tessera)) * [API](./api.md) - new privacy API diff --git a/docs/api.md b/docs/api.md index d7e2b84d0..45f731f64 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3,6 +3,8 @@ ## Privacy APIs +#### eth.sendTransaction + __To support private transactions in Quorum, the `web3.eth.sendTransaction(object)` API method has been modified.__ ```js @@ -50,6 +52,55 @@ web3.eth.sendTransaction({ ``` *** +#### eth.sendRawPrivateTransaction + +__To support sending raw transactions in Quorum, the `web3.eth.sendRawPrivateTransaction(string, object)` API method has been created.__ + +```js +web3.eth.sendRawPrivateTransaction(signedTransactionData [, privateData] [, callback]) +``` + +Sends a pre-signed transaction. For example can be signed using: https://github.com/SilentCicero/ethereumjs-accounts + +__Important:__ Please note that before calling this API, a `storeraw` api need to be called first to Quorum's private transaction manager. Instructions on how to do this can be found [here](https://github.com/jpmorganchase/tessera/wiki/Interface-&-API). + +##### Parameters + 1. `String` - Signed transaction data in HEX format + 2. `Object` - Private data to send + - `privateFor`: `List` - When sending a private transaction, an array of the recipients' base64-encoded public keys. +3. `Function` - (optional) If you pass a callback the HTTP request is made asynchronous. See [this note](#using-callbacks) for details. + + ##### Returns + `String` - The 32 Bytes transaction hash as HEX string. + If the transaction was a contract creation use [web3.eth.getTransactionReceipt()](#web3ethgettransactionreceipt) to get the contract address, after the transaction was mined. + + + ##### Example + ```js + var Tx = require('ethereumjs-tx'); + var privateKey = new Buffer('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', 'hex') + var rawTx = { + nonce: '0x00', + gasPrice: '0x09184e72a000', + gasLimit: '0x2710', + to: '0x0000000000000000000000000000000000000000', + value: '0x00', + // This data should be the hex value of the hash returned by Quorum's privacy transaction manager after invoking storeraw api + data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057' + } + var tx = new Tx(rawTx); + tx.sign(privateKey); + var serializedTx = tx.serialize(); + //console.log(serializedTx.toString('hex')); + //f889808609184e72a00082271094000000000000000000000000000000000000000080a47f74657374320000000000000000000000000000000000000000000000000000006000571ca08a8bbf888cfa37bbf0bb965423625641fc956967b81d12e23709cead01446075a01ce999b56a8a88504be365442ea61239198e23d1fce7d00fcfc5cd3b44b7215f + web3.eth.sendRawPrivateTransaction('0x' + serializedTx.toString('hex'), {privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}, function(err, hash) { + if (!err) + console.log(hash); // "0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385" + }); + ``` + + + ## JSON RPC Privacy API Reference __In addition to the JSON-RPC provided by Ethereum, Quorum exposes below two API calls.__ diff --git a/docs/istanbul-rpc-api.md b/docs/istanbul-rpc-api.md new file mode 100644 index 000000000..baa8302db --- /dev/null +++ b/docs/istanbul-rpc-api.md @@ -0,0 +1,86 @@ +# Istanbul RPC API +This is an up to date copy of original wiki entry located here https://github.com/getamis/go-ethereum/wiki/RPC-API + + +# Getting Started +1. Run Istanbul geth with `--rpcapi "istanbul"` +2. `geth attach` + +## API Reference + +### istanbul.candidates +Candidates returns the current candidates which the node tries to vote in or out. +``` +istanbul.candidates +``` + +#### Returns +`map[string] boolean` - returns the current candidates map. + +### istanbul.discard +Discard drops a currently running candidate, stopping the validator from casting further votes (either for or against). +``` +istanbul.discard(address) +``` + +#### Parameters +`string` - the address of the candidate + +### istanbul.getSnapshot +GetSnapshot retrieves the state snapshot at a given block. +``` +istanbul.getSnapshot(blockHashOrBlockNumber) +``` + +#### Parameters +`String|Number` - The block number, the string "latest" or nil. nil is the same with string "latest" and means the latest block + +#### Returns +`Object` - The snapshot object + +### istanbul.getSnapshotAtHash +GetSnapshotAtHash retrieves the state snapshot at a given block. +``` +istanbul.getSnapshotAtHash(blockHash) +``` + +#### Parameters +`String` - The block hash + +#### Returns +`Object` - The snapshot object + +### istanbul.getValidators +GetValidators retrieves the list of authorized validators at the specified block. +``` +istanbul.getValidators(blockHashOrBlockNumber) +``` + +#### Parameters +`String|Number` - The block number, the string "latest" or nil. nil is the same with string "latest" and means the latest block + +#### Returns +`[]string` - The validator address array + +### istanbul.getValidatorsAtHash +GetValidatorsAtHash retrieves the list of authorized validators at the specified block. +``` +istanbul.getValidatorsAtHash(blockHash) +``` + +#### Parameters +`String` - The block hash + +#### Returns +`[]string` - The validator address array + +### istanbul.propose +Propose injects a new authorization candidate that the validator will attempt to push through. If the number of vote is larger than 1/2 of validators to vote in/out, the candidate will be added/removed in validator set. + +``` +istanbul.propose(address, auth) +``` + +#### Parameters +`String` - The address of candidate +`bool` - `true` votes in and `false` votes out diff --git a/raft/doc.md b/docs/raft.md similarity index 100% rename from raft/doc.md rename to docs/raft.md diff --git a/eth/api_backend.go b/eth/api_backend.go index d88ce0b63..930c1d08e 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -150,7 +150,19 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state vm.M vmError := func() error { return nil } context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil) - return vm.NewEVM(context, statedb.state, statedb.privateState, b.eth.chainConfig, vmCfg), vmError, nil + + // Set the private state to public state if contract address is not present in the private state + to := common.Address{} + if msg.To() != nil { + to = *msg.To() + } + + privateState := statedb.privateState + if !privateState.Exist(to) { + privateState = statedb.state + } + + return vm.NewEVM(context, statedb.state, privateState, b.eth.chainConfig, vmCfg), vmError, nil } func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { diff --git a/eth/backend.go b/eth/backend.go index ea98f5930..30e45cfe6 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -378,12 +378,13 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { // set in js console via admin interface or wrapper from cli flags func (s *Ethereum) SetEtherbase(etherbase common.Address) { s.lock.Lock() + defer s.lock.Unlock() if _, ok := s.engine.(consensus.Istanbul); ok { log.Error("Cannot set etherbase in Istanbul consensus") return } s.etherbase = etherbase - s.lock.Unlock() + s.miner.SetEtherbase(etherbase) } diff --git a/eth/bloombits.go b/eth/bloombits.go index 954239d14..d846dc882 100644 --- a/eth/bloombits.go +++ b/eth/bloombits.go @@ -121,10 +121,14 @@ func (b *BloomIndexer) Reset(section uint64, lastSectionHead common.Hash) error return err } -// Process implements core.ChainIndexerBackend, adding a new header's bloom into -// the index. +// Process implements core.ChainIndexerBackend, executes an Or operation on header.bloom and private bloom +// (header.bloom | private bloom) and adds to index func (b *BloomIndexer) Process(header *types.Header) { - b.gen.AddBloom(uint(header.Number.Uint64()-b.section*b.size), header.Bloom) + publicBloom := header.Bloom + privateBloom := core.GetPrivateBlockBloom(b.db, header.Number.Uint64()) + publicBloom.OrBloom(privateBloom.Bytes()) + + b.gen.AddBloom(uint(header.Number.Uint64()-b.section*b.size), publicBloom) b.head = header.Hash() } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a2c06e8c0..34edae940 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1171,12 +1171,17 @@ type SendTxArgs struct { Input *hexutil.Bytes `json:"input"` //Quorum - PrivateFrom string `json:"privateFrom"` - PrivateFor []string `json:"privateFor"` - PrivateTxType string `json:"restriction"` + PrivateFrom string `json:"privateFrom"` + PrivateFor []string `json:"privateFor"` + PrivateTxType string `json:"restriction"` //End-Quorum } +// SendRawTxArgs represents the arguments to submit a new signed private transaction into the transaction pool. +type SendRawTxArgs struct { + PrivateFor []string `json:"privateFor"` +} + // setDefaults is a helper function that fills in default values for unspecified tx fields. func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error { if args.Gas == nil { @@ -1321,6 +1326,35 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return submitTransaction(ctx, s.b, tx, tx.IsPrivate()) } +// SendRawPrivateTransaction will add the signed transaction to the transaction pool. +// The sender is responsible for signing the transaction and using the correct nonce. +func (s *PublicTransactionPoolAPI) SendRawPrivateTransaction(ctx context.Context, encodedTx hexutil.Bytes, args SendRawTxArgs) (common.Hash, error) { + + tx := new(types.Transaction) + if err := rlp.DecodeBytes(encodedTx, tx); err != nil { + return common.Hash{}, err + } + + txHash := []byte(tx.Data()) + isPrivate := args.PrivateFor != nil + + if isPrivate { + if len(txHash) > 0 { + //Send private transaction to privacy manager + log.Info("sending private tx", "data", fmt.Sprintf("%x", txHash), "privatefor", args.PrivateFor) + result, err := private.P.SendSignedTx(txHash, args.PrivateFor) + log.Info("sent private tx", "result", fmt.Sprintf("%x", result), "privatefor", args.PrivateFor) + if err != nil { + return common.Hash{}, err + } + } + } else { + return common.Hash{}, fmt.Errorf("transaction is not private") + } + + return submitTransaction(ctx, s.b, tx, isPrivate) +} + // Sign calculates an ECDSA signature for: // keccack256("\x19Ethereum Signed Message:\n" + len(message) + message). // @@ -1694,6 +1728,7 @@ func (s *PublicBlockChainAPI) GetQuorumPayload(digestHex string) (string, error) } return fmt.Sprintf("0x%x", data), nil } + //End-Quorum func resolvePrivateFor(privateFor []string) []string{ diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 061b981cd..795dc8b6f 100755 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -399,6 +399,12 @@ const Eth_JS = ` web3._extend({ property: 'eth', methods: [ + new web3._extend.Method({ + name: 'sendRawPrivateTransaction', + call: 'eth_sendRawPrivateTransaction', + params: 2, + inputFormatter: [null, null] + }), new web3._extend.Method({ name: 'sign', call: 'eth_sign', diff --git a/miner/worker.go b/miner/worker.go index d6450f8bd..4ddc3b9a4 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -344,6 +344,12 @@ func (self *worker) wait() { log.Error("Failed writWriteBlockAndStating block to chain", "err", err) continue } + + if err := core.WritePrivateBlockBloom(self.chainDb, block.NumberU64(), work.privateReceipts); err != nil { + log.Error("Failed writing private block bloom", "err", err) + continue + } + // Broadcast the block and announce chain insertion event self.mux.Post(core.NewMinedBlockEvent{Block: block}) var ( diff --git a/p2p/permissions.go b/p2p/permissions.go index c07a4f618..520bb4cc3 100644 --- a/p2p/permissions.go +++ b/p2p/permissions.go @@ -35,7 +35,6 @@ func isNodePermissioned(nodename string, currentNode string, datadir string, dir } return true } - log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "DENIED-BY", currentNode[:NODE_NAME_LENGTH]) } log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "DENIED-BY", currentNode[:NODE_NAME_LENGTH]) return false diff --git a/params/protocol_params.go b/params/protocol_params.go index 1ee477821..2ba50e967 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -20,9 +20,15 @@ import "math/big" var ( TargetGasLimit = GenesisGasLimit // The artificial target + ) const ( + // these are original values from upstream Geth, used in ethash consensus + OriginnalMinGasLimit uint64 = 5000 // The bound divisor of the gas limit, used in update calculations. + OriginalGasLimitBoundDivisor uint64 = 1024 // Minimum the gas limit may ever be. + + // modified values for Quorum GasLimitBoundDivisor uint64 = 4096 // The bound divisor of the gas limit, used in update calculations. MinGasLimit uint64 = 700000000 // Minimum the gas limit may ever be. GenesisGasLimit uint64 = 800000000 // Gas limit of the Genesis block. diff --git a/params/version.go b/params/version.go index 758810dc7..358e50479 100644 --- a/params/version.go +++ b/params/version.go @@ -27,8 +27,8 @@ const ( VersionMeta = "stable" // Version metadata to append to the version string QuorumVersionMajor = 2 - QuorumVersionMinor = 1 - QuorumVersionPatch = 1 + QuorumVersionMinor = 2 + QuorumVersionPatch = 0 ) // Version holds the textual version string. diff --git a/private/constellation/constellation.go b/private/constellation/constellation.go index 69afcde68..3cc9aa62c 100644 --- a/private/constellation/constellation.go +++ b/private/constellation/constellation.go @@ -33,6 +33,18 @@ func (g *Constellation) Send(data []byte, from string, to []string) (out []byte, return out, nil } +func (g *Constellation) SendSignedTx(data []byte, to []string) (out []byte, err error) { + if g.isConstellationNotInUse { + return nil, ErrConstellationIsntInit + } + out, err = g.node.SendSignedPayload(data, to) + if err != nil { + return nil, err + } + return out, nil +} + + func (g *Constellation) Receive(data []byte) ([]byte, error) { if g.isConstellationNotInUse { return nil, nil diff --git a/private/constellation/node.go b/private/constellation/node.go index ce4a420eb..50263aae6 100644 --- a/private/constellation/node.go +++ b/private/constellation/node.go @@ -106,6 +106,30 @@ func (c *Client) SendPayload(pl []byte, b64From string, b64To []string) ([]byte, return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, res.Body)) } +func (c *Client) SendSignedPayload(signedPayload []byte, b64To []string) ([]byte, error) { + buf := bytes.NewBuffer(signedPayload) + req, err := http.NewRequest("POST", "http+unix://c/sendsignedtx", buf) + if err != nil { + return nil, err + } + + req.Header.Set("c11n-to", strings.Join(b64To, ",")) + req.Header.Set("Content-Type", "application/octet-stream") + res, err := c.httpClient.Do(req) + + if res != nil { + defer res.Body.Close() + } + if err != nil { + return nil, err + } + if res.StatusCode != 200 { + return nil, fmt.Errorf("Non-200 status code: %+v", res) + } + + return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, res.Body)) +} + func (c *Client) ReceivePayload(key []byte) ([]byte, error) { req, err := http.NewRequest("GET", "http+unix://c/receiveraw", nil) if err != nil { diff --git a/private/private.go b/private/private.go index 2cf2175bf..bcbad08da 100644 --- a/private/private.go +++ b/private/private.go @@ -8,6 +8,7 @@ import ( type PrivateTransactionManager interface { Send(data []byte, from string, to []string) ([]byte, error) + SendSignedTx(data []byte, to []string) ([]byte, error) Receive(data []byte) ([]byte, error) } diff --git a/tests/testdata b/tests/testdata index 2bb0c3da3..95b69d264 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 2bb0c3da3bbb15c528bcef2a7e5ac4bd73f81f87 +Subproject commit 95b69d2641f50c48862941f425c007b1ed243259