Add temporary PrivateTransactionManager integration

This commit is contained in:
Patrick Mylund Nielsen 2016-11-14 00:16:20 -05:00
parent 5fed1e349f
commit 13eb8db520
7 changed files with 338 additions and 12 deletions

View File

@ -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)
}

View File

@ -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

View File

@ -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())

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

22
private/private.go Normal file
View File

@ -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")