mirror of https://github.com/poanetwork/quorum.git
merge with master
This commit is contained in:
commit
061c9b85ce
|
@ -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:
|
||||
|
||||
```
|
||||
|
|
|
@ -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 <jeffrey@ethereum.org>
|
||||
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 {
|
|
@ -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). <span style="background-color: #ffffbf">Please note</span> 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
|
||||
|
|
|
@ -820,18 +820,13 @@ 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 {
|
||||
if err := fdlimit.Raise(uint64(limit)); 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
|
||||
}
|
||||
return limit / 2 // Leave half for networking and other stuff
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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[:])
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
51
docs/api.md
51
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<String>` - 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.__
|
||||
|
|
|
@ -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
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -1177,6 +1177,11 @@ type SendTxArgs struct {
|
|||
//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{
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2bb0c3da3bbb15c528bcef2a7e5ac4bd73f81f87
|
||||
Subproject commit 95b69d2641f50c48862941f425c007b1ed243259
|
Loading…
Reference in New Issue