diff --git a/definitions/pipe.go b/definitions/pipe.go index ffbec38dac807769ef9719759ca142976ed29e48..193eaed64942773f0f53bd483bbf25cd7aafcab1 100644 --- a/definitions/pipe.go +++ b/definitions/pipe.go @@ -77,6 +77,8 @@ type Transactor interface { fee int64) (*txs.Receipt, error) TransactAndHold(privKey, address, data []byte, gasLimit, fee int64) (*txs.EventDataCall, error) + Send(privKey, toAddress []byte, amount int64) (*txs.Receipt, error) + SendAndHold(privKey, toAddress []byte, amount int64) (*txs.Receipt, error) TransactNameReg(privKey []byte, name, data string, amount, fee int64) (*txs.Receipt, error) SignTx(tx txs.Tx, privAccounts []*account.PrivAccount) (txs.Tx, error) diff --git a/manager/eris-mint/transactor.go b/manager/eris-mint/transactor.go index 3b971d3702d72040d7132b7aba7f24d913686be7..3480d16a0dc7c02e49e1137f7b84183158072c29 100644 --- a/manager/eris-mint/transactor.go +++ b/manager/eris-mint/transactor.go @@ -252,6 +252,97 @@ func (this *transactor) TransactAndHold(privKey, address, data []byte, gasLimit, return ret, rErr } +func (this *transactor) Send(privKey, toAddress []byte, + amount int64) (*txs.Receipt, error) { + var toAddr []byte + if len(toAddress) == 0 { + toAddr = nil + } else if len(toAddress) != 20 { + return nil, fmt.Errorf("To-address is not of the right length: %d\n", + len(toAddress)) + } else { + toAddr = toAddress + } + + if len(privKey) != 64 { + return nil, fmt.Errorf("Private key is not of the right length: %d\n", + len(privKey)) + } + + pk := &[64]byte{} + copy(pk[:], privKey) + this.txMtx.Lock() + defer this.txMtx.Unlock() + pa := account.GenPrivAccountFromPrivKeyBytes(privKey) + cache := this.erisMint.GetState() + acc := cache.GetAccount(pa.Address) + var sequence int + if acc == nil { + sequence = 1 + } else { + sequence = acc.Sequence + 1 + } + + tx := txs.NewSendTx() + + txInput := &txs.TxInput{ + Address: pa.Address, + Amount: amount, + Sequence: sequence, + PubKey: pa.PubKey, + } + + tx.Inputs = append(tx.Inputs, txInput) + + txOutput := &txs.TxOutput{toAddr, amount} + + tx.Outputs = append(tx.Outputs, txOutput) + + // Got ourselves a tx. + txS, errS := this.SignTx(tx, []*account.PrivAccount{pa}) + if errS != nil { + return nil, errS + } + return this.BroadcastTx(txS) +} + +func (this *transactor) SendAndHold(privKey, toAddress []byte, + amount int64) (*txs.Receipt, error) { + rec, tErr := this.Send(privKey, toAddress, amount) + if tErr != nil { + return nil, tErr + } + + wc := make(chan *txs.SendTx) + subId := fmt.Sprintf("%X", rec.TxHash) + + this.eventEmitter.Subscribe(subId, txs.EventStringAccOutput(toAddress), + func(evt txs.EventData) { + event := evt.(txs.EventDataTx) + tx := event.Tx.(*txs.SendTx) + wc <- tx + }) + + timer := time.NewTimer(300 * time.Second) + toChan := timer.C + + var rErr error + + pa := account.GenPrivAccountFromPrivKeyBytes(privKey) + + select { + case <-toChan: + rErr = fmt.Errorf("Transaction timed out. Hash: " + subId) + case e := <-wc: + if bytes.Equal(e.Inputs[0].Address, pa.Address) && e.Inputs[0].Amount == amount { + timer.Stop() + this.eventEmitter.Unsubscribe(subId) + return rec, rErr + } + } + return nil, rErr +} + func (this *transactor) TransactNameReg(privKey []byte, name, data string, amount, fee int64) (*txs.Receipt, error) { diff --git a/rpc/v0/methods.go b/rpc/v0/methods.go index e7a65045c620e31a7c2242ddbb790cece65bc549..eaa82ff06eda12db9a282e62de845b0fc506df22 100644 --- a/rpc/v0/methods.go +++ b/rpc/v0/methods.go @@ -43,6 +43,8 @@ const ( SIGN_TX = SERVICE_NAME + ".signTx" TRANSACT = SERVICE_NAME + ".transact" TRANSACT_AND_HOLD = SERVICE_NAME + ".transactAndHold" + SEND = SERVICE_NAME + ".send" + SEND_AND_HOLD = SERVICE_NAME + ".sendAndHold" TRANSACT_NAMEREG = SERVICE_NAME + ".transactNameReg" EVENT_SUBSCRIBE = SERVICE_NAME + ".eventSubscribe" // Events EVENT_UNSUBSCRIBE = SERVICE_NAME + ".eventUnsubscribe" @@ -108,6 +110,8 @@ func (erisDbMethods *ErisDbMethods) getMethods() map[string]RequestHandlerFunc { dhMap[SIGN_TX] = erisDbMethods.SignTx dhMap[TRANSACT] = erisDbMethods.Transact dhMap[TRANSACT_AND_HOLD] = erisDbMethods.TransactAndHold + dhMap[SEND] = erisDbMethods.Send + dhMap[SEND_AND_HOLD] = erisDbMethods.SendAndHold dhMap[TRANSACT_NAMEREG] = erisDbMethods.TransactNameReg // Namereg dhMap[GET_NAMEREG_ENTRY] = erisDbMethods.NameRegEntry @@ -379,6 +383,32 @@ func (erisDbMethods *ErisDbMethods) TransactAndHold(request *rpc.RPCRequest, req return ce, 0, nil } +func (this *ErisDbMethods) Send(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { + param := &SendParam{} + err := this.codec.DecodeBytes(param, request.Params) + if err != nil { + return nil, rpc.INVALID_PARAMS, err + } + receipt, errC := this.pipe.Transactor().Send(param.PrivKey, param.ToAddress, param.Amount) + if errC != nil { + return nil, rpc.INTERNAL_ERROR, errC + } + return receipt, 0, nil +} + +func (this *ErisDbMethods) SendAndHold(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { + param := &SendParam{} + err := this.codec.DecodeBytes(param, request.Params) + if err != nil { + return nil, rpc.INVALID_PARAMS, err + } + rec, errC := this.pipe.Transactor().SendAndHold(param.PrivKey, param.ToAddress, param.Amount) + if errC != nil { + return nil, rpc.INTERNAL_ERROR, errC + } + return rec, 0, nil +} + func (erisDbMethods *ErisDbMethods) TransactNameReg(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { param := &TransactNameRegParam{} err := erisDbMethods.codec.DecodeBytes(param, request.Params) diff --git a/rpc/v0/params.go b/rpc/v0/params.go index 08b93079bcad641660c889afc6543ad7d890e9f1..98778554dec9a3a2329d0d591b8cd5b05f21e12f 100644 --- a/rpc/v0/params.go +++ b/rpc/v0/params.go @@ -90,6 +90,13 @@ type ( GasLimit int64 `json:"gas_limit"` } + // Used when sending a 'Send' transaction. + SendParam struct { + PrivKey []byte `json:"priv_key"` + ToAddress []byte `json:"to_address"` + Amount int64 `json:"amount"` + } + NameRegEntryParam struct { Name string `json:"name"` }