mirror of https://github.com/poanetwork/quorum.git
Add temporary PrivateTransactionManager integration
This commit is contained in:
parent
5fed1e349f
commit
13eb8db520
|
@ -21,10 +21,12 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/private"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -234,17 +236,27 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
|||
msg := self.msg
|
||||
sender, _ := self.from() // err checked in preCheck
|
||||
|
||||
var data []byte
|
||||
if tx, ok := msg.(*types.Transaction); ok && tx.IsPrivate() {
|
||||
data, err = private.P.Receive(self.data)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
} else {
|
||||
data = self.data
|
||||
}
|
||||
|
||||
homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber())
|
||||
contractCreation := MessageCreatesContract(msg)
|
||||
// Pay intrinsic gas
|
||||
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
|
||||
if err = self.useGas(IntrinsicGas(data, contractCreation, homestead)); err != nil {
|
||||
return nil, nil, nil, InvalidTxError(err)
|
||||
}
|
||||
|
||||
vmenv := self.env
|
||||
//var addr common.Address
|
||||
if contractCreation {
|
||||
ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
|
||||
ret, _, err = vmenv.Create(sender, data, self.gas, self.gasPrice, self.value)
|
||||
if homestead && err == vm.CodeStoreOutOfGasError {
|
||||
self.gas = Big0
|
||||
}
|
||||
|
@ -260,7 +272,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
|||
}
|
||||
// Increment the nonce for the next transaction
|
||||
state.SetNonce(sender.Address(), state.GetNonce(sender.Address())+1)
|
||||
ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value)
|
||||
ret, err = vmenv.Call(sender, self.to().Address(), data, self.gas, self.gasPrice, self.value)
|
||||
if err != nil {
|
||||
glog.V(logger.Core).Infoln("VM call err:", err)
|
||||
}
|
||||
|
|
|
@ -299,6 +299,14 @@ func (tx *Transaction) IsPrivate() bool {
|
|||
return tx.data.V == 37 || tx.data.V == 38
|
||||
}
|
||||
|
||||
func (tx *Transaction) SetPrivate() {
|
||||
if tx.data.V == 28 {
|
||||
tx.data.V = 38
|
||||
} else {
|
||||
tx.data.V = 37
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *Transaction) publicKey(homestead bool) ([]byte, error) {
|
||||
if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) {
|
||||
return nil, ErrInvalidSig
|
||||
|
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/private"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
|
@ -994,13 +995,15 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti
|
|||
|
||||
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
|
||||
type SendTxArgs struct {
|
||||
From common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *rpc.HexNumber `json:"gas"`
|
||||
GasPrice *rpc.HexNumber `json:"gasPrice"`
|
||||
Value *rpc.HexNumber `json:"value"`
|
||||
Data string `json:"data"`
|
||||
Nonce *rpc.HexNumber `json:"nonce"`
|
||||
From common.Address `json:"from"`
|
||||
To *common.Address `json:"to"`
|
||||
Gas *rpc.HexNumber `json:"gas"`
|
||||
GasPrice *rpc.HexNumber `json:"gasPrice"`
|
||||
Value *rpc.HexNumber `json:"value"`
|
||||
Data string `json:"data"`
|
||||
Nonce *rpc.HexNumber `json:"nonce"`
|
||||
PrivateFrom string `json:"privateFrom"`
|
||||
PrivateFor []string `json:"privateFor"`
|
||||
}
|
||||
|
||||
// prepareSendTxArgs is a helper function that fills in default values for unspecified tx fields.
|
||||
|
@ -1061,10 +1064,17 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
|
|||
}
|
||||
|
||||
var tx *types.Transaction
|
||||
data := common.FromHex(args.Data)
|
||||
if len(args.PrivateFor) > 0 {
|
||||
data, err = private.P.Send(data, args.PrivateFrom, args.PrivateFor)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
}
|
||||
if args.To == nil {
|
||||
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
||||
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), data)
|
||||
} else {
|
||||
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
||||
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), data)
|
||||
}
|
||||
|
||||
signature, err := s.b.AccountManager().SignEthereum(args.From, tx.SigHash().Bytes())
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package constellation
|
||||
|
||||
import (
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Url string `toml:"url"`
|
||||
Port int `toml:"port"`
|
||||
SocketPath string `toml:"socketPath"`
|
||||
OtherNodeUrls []string `toml:"otherNodeUrls"`
|
||||
PublicKeyPath string `toml:"publicKeyPath"`
|
||||
PrivateKeyPath string `toml:"privateKeyPath"`
|
||||
StoragePath string `toml:"storagePath"`
|
||||
}
|
||||
|
||||
func LoadConfig(configPath string) (*Config, error) {
|
||||
cfg := new(Config)
|
||||
if _, err := toml.DecodeFile(configPath, cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package constellation
|
||||
|
||||
import (
|
||||
"github.com/patrickmn/go-cache"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func copyBytes(b []byte) []byte {
|
||||
ob := make([]byte, len(b))
|
||||
copy(ob, b)
|
||||
return ob
|
||||
}
|
||||
|
||||
type Constellation struct {
|
||||
node *Client
|
||||
c *cache.Cache
|
||||
}
|
||||
|
||||
func (g *Constellation) Send(data []byte, from string, to []string) (out []byte, err error) {
|
||||
if len(data) > 0 {
|
||||
if len(to) == 0 {
|
||||
out = copyBytes(data)
|
||||
} else {
|
||||
var err error
|
||||
out, err = g.node.SendPayload(data, from, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
g.c.Set(string(out), data, cache.DefaultExpiration)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (g *Constellation) Receive(data []byte) ([]byte, error) {
|
||||
if len(data) == 0 {
|
||||
return data, nil
|
||||
}
|
||||
// Ignore this error since not being a recipient of
|
||||
// a payload isn't an error.
|
||||
// TODO: Return an error if it's anything OTHER than
|
||||
// 'you are not a recipient.'
|
||||
dataStr := string(data)
|
||||
x, found := g.c.Get(dataStr)
|
||||
if found {
|
||||
return x.([]byte), nil
|
||||
}
|
||||
pl, _ := g.node.ReceivePayload(data)
|
||||
g.c.Set(dataStr, pl, cache.DefaultExpiration)
|
||||
return pl, nil
|
||||
}
|
||||
|
||||
func New(configPath string) (*Constellation, error) {
|
||||
cfg, err := LoadConfig(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = RunNode(configPath, cfg.SocketPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n, err := NewClient(cfg.PublicKeyPath, cfg.SocketPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Constellation{
|
||||
node: n,
|
||||
c: cache.New(5 * time.Minute, 5 * time.Minute),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func MustNew(configPath string) *Constellation {
|
||||
g, err := New(configPath)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("MustNew error: %v", err))
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func MaybeNew(configPath string) *Constellation {
|
||||
if configPath == "" {
|
||||
return nil
|
||||
}
|
||||
return MustNew(configPath)
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package constellation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/tv42/httpunix"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
func launchNode(cfgPath string) (*exec.Cmd, error) {
|
||||
cmd := exec.Command("constellation-node", cfgPath)
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go io.Copy(os.Stderr, stderr)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func unixTransport(socketPath string) *httpunix.Transport {
|
||||
t := &httpunix.Transport{
|
||||
DialTimeout: 1 * time.Second,
|
||||
RequestTimeout: 5 * time.Second,
|
||||
ResponseHeaderTimeout: 5 * time.Second,
|
||||
}
|
||||
t.RegisterLocation("c", socketPath)
|
||||
return t
|
||||
}
|
||||
|
||||
func unixClient(socketPath string) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: unixTransport(socketPath),
|
||||
}
|
||||
}
|
||||
|
||||
func RunNode(cfgPath, nodeSocketPath string) error {
|
||||
// launchNode(cfgPath)
|
||||
c := unixClient(nodeSocketPath)
|
||||
res, err := c.Get("http+unix://c/upcheck")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode == 200 {
|
||||
return nil
|
||||
}
|
||||
return errors.New("Constellation Node API did not respond to upcheck request")
|
||||
}
|
||||
|
||||
type SendRequest struct {
|
||||
Payload string `json:"payload"`
|
||||
From string `json:"from"`
|
||||
To []string `json:"to"`
|
||||
}
|
||||
|
||||
type SendResponse struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type ReceiveRequest struct {
|
||||
Key string `json:"key"`
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
type ReceiveResponse struct {
|
||||
Payload string `json:"payload"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
httpClient *http.Client
|
||||
publicKey [32]byte
|
||||
b64PublicKey string
|
||||
}
|
||||
|
||||
func (c *Client) do(path string, apiReq interface{}) (*http.Response, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
err := json.NewEncoder(buf).Encode(apiReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest("POST", "http+unix://c/" + path, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
res, err := c.httpClient.Do(req)
|
||||
if err == nil && res.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("Non-200 status code: %+v", res)
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (c *Client) SendPayload(pl []byte, b64From string, b64To []string) ([]byte, error) {
|
||||
var from string
|
||||
if b64From == "" {
|
||||
from = c.b64PublicKey
|
||||
} else {
|
||||
from = b64From
|
||||
}
|
||||
req := &SendRequest{
|
||||
Payload: base64.StdEncoding.EncodeToString(pl),
|
||||
From: from,
|
||||
To: b64To,
|
||||
}
|
||||
res, err := c.do("send", req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
sres := new(SendResponse)
|
||||
err = json.NewDecoder(res.Body).Decode(sres)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key, err := base64.StdEncoding.DecodeString(sres.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (c *Client) ReceivePayload(key []byte) ([]byte, error) {
|
||||
b64Key := base64.StdEncoding.EncodeToString(key)
|
||||
req := &ReceiveRequest{
|
||||
Key: b64Key,
|
||||
To: c.b64PublicKey,
|
||||
}
|
||||
res, err := c.do("receive", req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
rres := new(ReceiveResponse)
|
||||
err = json.NewDecoder(res.Body).Decode(rres)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pl, err := base64.StdEncoding.DecodeString(rres.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pl, nil
|
||||
}
|
||||
|
||||
func NewClient(publicKeyPath string, nodeSocketPath string) (*Client, error) {
|
||||
b64PublicKey, err := ioutil.ReadFile(publicKeyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Client{
|
||||
httpClient: unixClient(nodeSocketPath),
|
||||
b64PublicKey: string(b64PublicKey),
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package private
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/jpmorganchase/quorum/private/constellation"
|
||||
)
|
||||
|
||||
type PrivateTransactionManager interface {
|
||||
Send(data []byte, from string, to []string) ([]byte, error)
|
||||
Receive(data []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
func FromEnvironmentOrNil(name string) PrivateTransactionManager {
|
||||
cfgPath := os.Getenv(name)
|
||||
if cfgPath == "" {
|
||||
return nil
|
||||
}
|
||||
return constellation.MustNew(cfgPath)
|
||||
}
|
||||
|
||||
var P = FromEnvironmentOrNil("PRIVATE_CONFIG")
|
Loading…
Reference in New Issue