// Start Go API. Not important for this version func (c *Channels) NewChannel(key *ecdsa.PrivateKey, to common.Address, amount, price *big.Int, cb func(*Channel)) (*types.Transaction, error) { from := crypto.PubkeyToAddress(key.PublicKey) data, err := c.abi.Pack("createChannel", to, price) if err != nil { return nil, err } statedb, err := c.blockchain.State() if err != nil { return nil, err } transaction, err := types.NewTransaction(statedb.GetNonce(from), contractAddress, amount, big.NewInt(250000), big.NewInt(50000000000), data).SignECDSA(key) if err != nil { return nil, err } evId := c.abi.Events["NewChannel"].Id() filter := filters.New(c.db) filter.SetAddresses([]common.Address{contractAddress}) filter.SetTopics([][]common.Hash{ // TODO refactor, helper []common.Hash{evId}, []common.Hash{from.Hash()}, []common.Hash{to.Hash()}, }) filter.SetBeginBlock(0) filter.SetEndBlock(-1) filter.LogsCallback = func(logs vm.Logs) { // tere should really be only one log. TODO this part log := logs[0] // TODO: do to and from validation here /* from := log.Topics[1] to := log.Topics[2] */ channelId := common.BytesToHash(log.Data[0:31]) nonce := common.BytesToBig(log.Data[31:]) c.channelMu.Lock() defer c.channelMu.Unlock() channel, exist := c.channels[channelId] if !exist { channel = NewChannel(c, channelId, from, to, nonce) c.channels[channelId] = channel } cb(channel) } c.filters.Add(filter) return transaction, nil }
// exec is the executer function callback for the abi `Call` method. func (c *Channels) exec(input []byte) (out []byte) { header := c.blockchain.CurrentHeader() gasLimit := big.NewInt(3141592) statedb, _ := c.blockchain.State() var addr common.Address tx, _ := types.NewTransaction(statedb.GetNonce(addr), contractAddress, new(big.Int), gasLimit, new(big.Int), input).SignECDSA(c.callKey) env := core.NewEnv(statedb, c.blockchain, tx, header) ret, _, _ := core.ApplyMessage(env, tx, new(core.GasPool).AddGas(gasLimit)) return ret }
func (tx *Tx) UnmarshalJSON(b []byte) (err error) { req := struct { To common.Address `json:"to"` From common.Address `json:"from"` Nonce *rpc.HexNumber `json:"nonce"` Value *rpc.HexNumber `json:"value"` Data string `json:"data"` GasLimit *rpc.HexNumber `json:"gas"` GasPrice *rpc.HexNumber `json:"gasPrice"` Hash common.Hash `json:"hash"` }{} if err := json.Unmarshal(b, &req); err != nil { return err } contractCreation := (req.To == (common.Address{})) tx.To = &req.To tx.From = req.From tx.Nonce = req.Nonce tx.Value = req.Value tx.Data = req.Data tx.GasLimit = req.GasLimit tx.GasPrice = req.GasPrice tx.Hash = req.Hash data := common.Hex2Bytes(tx.Data) if tx.Nonce == nil { return fmt.Errorf("need nonce") } if tx.Value == nil { tx.Value = rpc.NewHexNumber(0) } if tx.GasLimit == nil { tx.GasLimit = rpc.NewHexNumber(0) } if tx.GasPrice == nil { tx.GasPrice = rpc.NewHexNumber(defaultGasPrice) } if contractCreation { tx.tx = types.NewContractCreation(tx.Nonce.Uint64(), tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data) } else { if tx.To == nil { return fmt.Errorf("need to address") } tx.tx = types.NewTransaction(tx.Nonce.Uint64(), *tx.To, tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data) } return nil }
// SendTransaction will create a transaction for the given transaction argument, sign it and submit it to the // transaction pool. func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash, error) { if args.Gas == nil { args.Gas = rpc.NewHexNumber(defaultGas) } if args.GasPrice == nil { args.GasPrice = rpc.NewHexNumber(defaultGasPrice) } if args.Value == nil { args.Value = rpc.NewHexNumber(0) } s.txMu.Lock() defer s.txMu.Unlock() if args.Nonce == nil { args.Nonce = rpc.NewHexNumber(s.txPool.State().GetNonce(args.From)) } var tx *types.Transaction contractCreation := (args.To == common.Address{}) if contractCreation { tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } else { tx = types.NewTransaction(args.Nonce.Uint64(), args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } signedTx, err := s.sign(args.From, tx) if err != nil { return common.Hash{}, err } s.txPool.SetLocal(signedTx) if err := s.txPool.Add(signedTx); err != nil { return common.Hash{}, nil } if contractCreation { addr := crypto.CreateAddress(args.From, args.Nonce.Uint64()) glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) } else { glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex()) } return signedTx.Hash(), nil }
// Claim redeems a given signature using the canonical channel. It creates an // Ethereum transaction and submits it to the Ethereum network. // // Chaim returns the unsigned transaction and an error if it failed. func (c *Channels) Claim(signer common.Address, from, to common.Address, nonce uint64, amount *big.Int, sig []byte) (*types.Transaction, error) { if len(sig) != 65 { return nil, fmt.Errorf("Invalid signature. Signature requires to be 65 bytes") } channelId := c.ChannelId(from, to) signature := bytesToSignature(sig) txData, err := c.abi.Pack("claim", channelId, nonce, amount, signature.v, signature.r, signature.s) if err != nil { return nil, err } statedb, _ := c.blockchain.State() gasPrice := big.NewInt(50000000000) gasLimit := big.NewInt(250000) tx := types.NewTransaction(statedb.GetNonce(signer), contractAddress, new(big.Int), gasLimit, gasPrice, txData) return tx, nil }
// SignTransaction will sign the given transaction with the from account. // The node needs to have the private key of the account corresponding with // the given from address and it needs to be unlocked. func (s *PublicTransactionPoolAPI) SignTransaction(args *SignTransactionArgs) (*SignTransactionResult, error) { if args.Gas == nil { args.Gas = rpc.NewHexNumber(defaultGas) } if args.GasPrice == nil { args.GasPrice = rpc.NewHexNumber(defaultGasPrice) } if args.Value == nil { args.Value = rpc.NewHexNumber(0) } s.txMu.Lock() defer s.txMu.Unlock() if args.Nonce == nil { args.Nonce = rpc.NewHexNumber(s.txPool.State().GetNonce(args.From)) } var tx *types.Transaction contractCreation := (args.To == common.Address{}) if contractCreation { tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } else { tx = types.NewTransaction(args.Nonce.Uint64(), args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } signedTx, err := s.sign(args.From, tx) if err != nil { return nil, err } data, err := rlp.EncodeToBytes(signedTx) if err != nil { return nil, err } return &SignTransactionResult{"0x" + common.Bytes2Hex(data), newTx(tx)}, nil }
// Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the // pool and reinsert it with the new gas price and limit. func (s *PublicTransactionPoolAPI) Resend(tx *Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { pending := s.txPool.GetTransactions() for _, p := range pending { if pFrom, err := p.From(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { if gasPrice == nil { gasPrice = rpc.NewHexNumber(tx.tx.GasPrice()) } if gasLimit == nil { gasLimit = rpc.NewHexNumber(tx.tx.Gas()) } var newTx *types.Transaction contractCreation := (*tx.tx.To() == common.Address{}) if contractCreation { newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) } else { newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) } signedTx, err := s.sign(tx.From, newTx) if err != nil { return common.Hash{}, err } s.txPool.RemoveTx(tx.Hash) if err = s.txPool.Add(signedTx); err != nil { return common.Hash{}, err } return signedTx.Hash(), nil } } return common.Hash{}, fmt.Errorf("Transaction %#x not found", tx.Hash) }
func main() { // Parse and handle the command line flags flag.Parse() log15.Root().SetHandler(log15.LvlFilterHandler(log15.Lvl(*loglevelFlag), log15.StderrHandler)) datadir := *datadirFlag if datadir == "" { datadir = filepath.Join(os.Getenv("HOME"), ".etherapis") } if err := os.MkdirAll(datadir, 0700); err != nil { log15.Crit("Failed to create data directory: %v", err) return } // Assemble and start the Ethereum client log15.Info("Booting Ethereum client...") client, err := geth.New(datadir, geth.TestNet) if err != nil { log15.Crit("Failed to create Ethereum client", "error", err) return } if err := client.Start(); err != nil { log15.Crit("Failed to start Ethereum client", "error", err) return } api, err := client.Attach() if err != nil { log15.Crit("Failed to attach to node", "error", err) return } // Wait for network connectivity and monitor synchronization log15.Info("Searching for network peers...") server := client.Stack().Server() for len(server.Peers()) == 0 { time.Sleep(100 * time.Millisecond) } go monitorSync(api) // Make sure we're at least semi recent on the chain before continuing waitSync(*syncFlag, api) var eth *eth.Ethereum err = client.Stack().Service(ð) if err != nil { log15.Crit("Failed to fetch eth service", "error", err) return } contract, err := channels.Fetch(eth.ChainDb(), eth.EventMux(), eth.BlockChain()) if err != nil { log15.Crit("Failed to get contract", "error", err) return } // Depending on the flags, execute different things switch { case *signFlag != "": var message struct { Provider string Nonce uint64 Amount uint64 } if err := json.Unmarshal([]byte(*signFlag), &message); err != nil { log15.Crit("Failed to decode data", "error", err) return } fmt.Println(message) accounts, err := eth.AccountManager().Accounts() if err != nil { log15.Crit("Failed retrieving accounts", "err", err) } if len(accounts) == 0 { log15.Crit("Signing data requires at least one account", "len", len(accounts)) return } account := accounts[0] from := account.Address to := common.HexToAddress(message.Provider) hash := contract.Call("getHash", from, to, message.Nonce, message.Amount).([]byte) log15.Info("getting hash", "hash", common.ToHex(hash)) eth.AccountManager().Unlock(from, "") sig, err := eth.AccountManager().Sign(account, hash) if err != nil { log15.Crit("signing vailed", "err", err) return } log15.Info("generated signature", "sig", common.ToHex(sig)) return case *importFlag != "": // Account import, parse the provided .json file and ensure it's proper manager := eth.AccountManager() account, err := manager.Import(*importFlag, "") if err != nil { log15.Crit("Failed to import specified account", "path", *importFlag, "error", err) return } state, _ := eth.BlockChain().State() log15.Info("Account successfully imported", "account", fmt.Sprintf("0x%x", account.Address), "balance", state.GetBalance(account.Address)) return case *accountsFlag: // Account listing requested, print all accounts and balances accounts, err := eth.AccountManager().Accounts() if err != nil || len(accounts) == 0 { log15.Crit("Failed to retrieve account", "accounts", len(accounts), "error", err) return } state, _ := eth.BlockChain().State() for i, account := range accounts { balance := float64(new(big.Int).Div(state.GetBalance(account.Address), common.Finney).Int64()) / 1000 fmt.Printf("Account #%d: %f ether (http://testnet.etherscan.io/address/0x%x)\n", i, balance, account.Address) } return case *accGenFlag > 0: // We're generating and dumping demo accounts var nonce uint64 var bank accounts.Account if *accLiveFlag { // If we want to fund generated accounts, make sure we can accounts, err := eth.AccountManager().Accounts() if err != nil || len(accounts) == 0 { log15.Crit("Failed to retrieve funding account", "accounts", len(accounts), "error", err) return } bank = accounts[0] if err := eth.AccountManager().Unlock(bank.Address, "gophergala"); err != nil { log15.Crit("Failed to unlock funding account", "account", fmt.Sprintf("0x%x", bank.Address), "error", err) return } state, _ := eth.BlockChain().State() nonce = state.GetNonce(bank.Address) log15.Info("Funding demo accounts with", "bank", fmt.Sprintf("0x%x", bank.Address), "nonce", nonce) } // Start generating the actual accounts log15.Info("Generating demo accounts", "count", *accGenFlag) for i := 0; i < *accGenFlag; i++ { // Generate a new account account, err := eth.AccountManager().NewAccount("pass") if err != nil { log15.Crit("Failed to generate new account", "error", err) return } // Export it's private key keyPath := fmt.Sprintf("0x%x.key", account.Address) if err := eth.AccountManager().Export(keyPath, account.Address, "pass"); err != nil { log15.Crit("Failed to export account", "account", fmt.Sprintf("0x%x", account.Address), "error", err) return } // Clean up so it doesn't clutter out accounts if err := eth.AccountManager().DeleteAccount(account.Address, "pass"); err != nil { log15.Crit("Failed to delete account", "account", fmt.Sprintf("0x%x", account.Address), "error", err) return } // If we're just testing, stop here if !*accLiveFlag { log15.Info("Account generated and exported", "path", keyPath) continue } // Oh boy, live accounts, send some ether to it and upload to the faucet allowance := new(big.Int).Mul(big.NewInt(10), common.Ether) price := new(big.Int).Mul(big.NewInt(50), common.Shannon) tx := types.NewTransaction(nonce, account.Address, allowance, big.NewInt(21000), price, nil) sig, err := eth.AccountManager().Sign(bank, tx.SigHash().Bytes()) if err != nil { log15.Crit("Failed to sign funding transaction", "error", err) return } stx, err := tx.WithSignature(sig) if err != nil { log15.Crit("Failed to assemble funding transaction", "error", err) return } if err := eth.TxPool().Add(stx); err != nil { log15.Crit("Failed to execute transfer", "error", err) return } nonce++ log15.Info("Account successfully funded", "account", fmt.Sprintf("0x%x", account.Address)) // Upload the account to the faucet server key, err := ioutil.ReadFile(keyPath) if err != nil { log15.Crit("Failed to load private key", "error", err) return } res, err := http.Get("https://etherapis.appspot.com/faucet/fund?key=" + string(key)) if err != nil { log15.Crit("Failed to upload private key to faucet", "error", err) return } res.Body.Close() log15.Info("Account uploaded to faucet", "account", fmt.Sprintf("0x%x", account.Address)) os.Remove(keyPath) } // Just wait a bit to ensure transactions get propagated into the network log15.Info("Sleeping to ensure transaction propagation") time.Sleep(10 * time.Second) return case len(*queryFlag) > 0: // Check whether any of our accounts are subscribed to this particular service accounts, err := eth.AccountManager().Accounts() if err != nil || len(accounts) == 0 { log15.Crit("Failed to retrieve account", "accounts", len(accounts), "error", err) return } provider := common.HexToAddress(*queryFlag) log15.Info("Checking subscription status", "service", fmt.Sprintf("0x%x", provider)) for i, account := range accounts { // Check if a subscription exists if !contract.Exists(account.Address, provider) { fmt.Printf("Account #%d: [0x%x]: not subscribed.\n", i, account.Address) continue } // Retrieve the current balance on the subscription ethers := contract.Call("getChannelValue", contract.ChannelId(account.Address, provider)).(*big.Int) funds := float64(new(big.Int).Div(ethers, common.Finney).Int64()) / 1000 fmt.Printf("Account #%d: [0x%x]: subscribed, with %v ether(s) left.\n", i, account.Address, funds) } return case len(*subToFlag) > 0: // Subscription requested, make sure all the details are provided accounts, err := eth.AccountManager().Accounts() if err != nil || len(accounts) < *subAccFlag { log15.Crit("Failed to retrieve account", "accounts", len(accounts), "requested", *subAccFlag, "error", err) return } account := accounts[*subAccFlag] // Check if a subscription exists provider := common.HexToAddress(*subToFlag) if contract.Exists(account.Address, provider) { log15.Error("Account already subscribed", "index", *subAccFlag, "account", fmt.Sprintf("0x%x", account.Address), "service", fmt.Sprintf("0x%x", provider)) return } // Try to subscribe and wait until it completes keystore := client.Keystore() key, err := keystore.GetKey(account.Address, "") if err != nil { log15.Crit("Failed to unlock account", "account", fmt.Sprintf("0x%x", account.Address), "error", err) return } amount := new(big.Int).Mul(big.NewInt(int64(1000000000**subFundFlag)), common.Shannon) log15.Info("Subscribing to new payment channel", "account", fmt.Sprintf("0x%x", account.Address), "service", fmt.Sprintf("0x%x", provider), "ethers", *subFundFlag) pend := make(chan *channels.Channel) tx, err := contract.NewChannel(key.PrivateKey, provider, amount, big.NewInt(1), func(sub *channels.Channel) { pend <- sub }) if err != nil { log15.Crit("Failed to create subscription", "error", err) return } if err := eth.TxPool().Add(tx); err != nil { log15.Crit("Failed to execute subscription", "error", err) return } log15.Info("Waiting for subscription to be finalized...", "tx", tx) log15.Info("Successfully subscribed", "channel", fmt.Sprintf("%x", (<-pend).Id)) return } if *testFlag { accounts, err := eth.AccountManager().Accounts() if err != nil { log15.Crit("Failed retrieving accounts", "err", err) } if len(accounts) < 2 { log15.Crit("Test vectors requires at least 2 accounts", "len", len(accounts)) return } log15.Info("Attempting channel test vectors...") from := accounts[0].Address eth.AccountManager().Unlock(from, "") to := accounts[0].Address log15.Info("making channel name...", "from", from.Hex(), "to", to.Hex(), "ID", contract.ChannelId(from, to).Hex()) log15.Info("checking existence...", "exists", contract.Exists(from, to)) amount := big.NewInt(1) hash := contract.Call("getHash", from, to, 0, amount).([]byte) log15.Info("signing data", "to", to.Hex(), "amount", amount, "hash", common.ToHex(hash)) sig, err := eth.AccountManager().Sign(accounts[0], hash) if err != nil { log15.Crit("signing vailed", "err", err) return } log15.Info("verifying signature", "sig", common.ToHex(sig)) if contract.ValidateSig(from, to, 0, amount, sig) { log15.Info("signature was valid and was verified by the EVM") } else { log15.Crit("signature was invalid") return } log15.Info("verifying payment", "sig", common.ToHex(sig)) if valid, _ := contract.Verify(from, to, 0, amount, sig); valid { log15.Info("payment was valid and was verified by the EVM") } else { log15.Crit("payment was invalid") return } log15.Info("verifying invalid payment", "nonce", 1) if valid, _ := contract.Verify(from, to, 1, amount, sig); valid { log15.Crit("payment was valid") return } else { log15.Info("payment was invalid") } } // If we're running a proxy, start processing external requests if *proxyFlag != "" { // Subscription requested, make sure all the details are provided accounts, err := eth.AccountManager().Accounts() if err != nil || len(accounts) < *subAccFlag { log15.Crit("Failed to retrieve account", "accounts", len(accounts), "requested", *subAccFlag, "error", err) return } account := accounts[*subAccFlag] if err := eth.AccountManager().Unlock(account.Address, ""); err != nil { log15.Crit("Failed to unlock provider account", "account", fmt.Sprintf("0x%x", account.Address), "error", err) return } log15.Info("Setuping vault...", "owner", account.Address.Hex()) // Create the payment vault to hold the various authorizations vault := proxy.NewVault(NewCharger(account, eth.TxPool(), contract, eth.AccountManager())) vault.AutoCharge(*chargeFlag) for i, config := range strings.Split(*proxyFlag, ",") { // Split the proxy configuration parts := strings.Split(config, ":") if len(parts) != 3 { log15.Crit("Invalid proxy config", "config", config) return } extPort, err := strconv.Atoi(parts[0]) if err != nil || extPort < 0 || extPort > 65535 { log15.Crit("Invalid external port number", "port", parts[0]) return } intPort, err := strconv.Atoi(parts[1]) if err != nil || intPort < 0 || intPort > 65535 { log15.Crit("Invalid internal port number", "port", parts[1]) return } var kind proxy.ProxyType switch strings.ToLower(parts[2]) { case "call": kind = proxy.CallProxy case "data": kind = proxy.DataProxy default: log15.Crit("Unsupported proxy type", "type", parts[2], "allowed", []string{"call", "data"}) return } // Create and start the new proxy gateway := proxy.New(i, extPort, intPort, kind, contract, vault) go func() { if err := gateway.Start(); err != nil { log15.Crit("Failed to start proxy", "error", err) os.Exit(-1) } }() } // Wait indefinitely, for now at least for { time.Sleep(time.Second) } } // Clean up for now log15.Info("Terminating Ethereum client...") if err := client.Stop(); err != nil { log15.Crit("Failed to terminate Ethereum client", "error", err) return } }
// Transact forms a transaction from the given arguments and submits it to the // transactio pool for execution. func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) { return "", errors.New("invalid address") } var ( from = common.HexToAddress(fromStr) to = common.HexToAddress(toStr) value = common.Big(valueStr) gas *big.Int price *big.Int data []byte contractCreation bool ) if len(gasStr) == 0 { gas = big.NewInt(90000) } else { gas = common.Big(gasStr) } if len(gasPriceStr) == 0 { price = big.NewInt(10000000000000) } else { price = common.Big(gasPriceStr) } data = common.FromHex(codeStr) if len(toStr) == 0 { contractCreation = true } nonce := be.txPool.State().GetNonce(from) if len(nonceStr) != 0 { nonce = common.Big(nonceStr).Uint64() } var tx *types.Transaction if contractCreation { tx = types.NewContractCreation(nonce, value, gas, price, data) } else { tx = types.NewTransaction(nonce, to, value, gas, price, data) } acc := accounts.Account{from} signature, err := be.am.Sign(acc, tx.SigHash().Bytes()) if err != nil { return "", err } signedTx, err := tx.WithSignature(signature) if err != nil { return "", err } be.txPool.SetLocal(signedTx) if err := be.txPool.Add(signedTx); err != nil { return "", nil } if contractCreation { addr := crypto.CreateAddress(from, nonce) glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) } else { glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex()) } return signedTx.Hash().Hex(), nil }