type spendAction struct {
accounts *Manager
bc.AssetAmount
- AccountID string `json:"account_id"`
- ClientToken *string `json:"client_token"`
+ AccountID string `json:"account_id"`
}
// MergeSpendAction merge common assetID and accountID spend action
AssetID: *a.AssetId,
AccountID: a.AccountID,
}
- res, err := a.accounts.utxoDB.Reserve(src, a.Amount, a.ClientToken, b.MaxTime())
+ res, err := a.accounts.utxoDB.Reserve(src, a.Amount, b.MaxTime())
if err != nil {
return errors.Wrap(err, "reserving utxos")
}
}
type spendUTXOAction struct {
- accounts *Manager
- OutputID *bc.Hash `json:"output_id"`
- Arguments []contractArgument `json:"arguments"`
- ClientToken *string `json:"client_token"`
+ accounts *Manager
+ OutputID *bc.Hash `json:"output_id"`
+ Arguments []contractArgument `json:"arguments"`
}
// contractArgument for smart contract
return txbuilder.MissingFieldsError("output_id")
}
- res, err := a.accounts.utxoDB.ReserveUTXO(ctx, *a.OutputID, a.ClientToken, b.MaxTime())
+ res, err := a.accounts.utxoDB.ReserveUTXO(ctx, *a.OutputID, b.MaxTime())
if err != nil {
return err
}
return err
}
- if a.Arguments != nil {
- sigInst = &txbuilder.SigningInstruction{}
- for _, arg := range a.Arguments {
- switch arg.Type {
- case "raw_tx_signature":
- rawTxSig := &rawTxSigArgument{}
- if err = json.Unmarshal(arg.RawData, rawTxSig); err != nil {
- return err
- }
-
- // convert path form chainjson.HexBytes to byte
- var path [][]byte
- for _, p := range rawTxSig.Path {
- path = append(path, []byte(p))
- }
- sigInst.AddRawWitnessKeys([]chainkd.XPub{rawTxSig.RootXPub}, path, 1)
-
- case "data":
- data := &dataArgument{}
- if err = json.Unmarshal(arg.RawData, data); err != nil {
- return err
- }
-
- value, err := hex.DecodeString(data.Value)
- if err != nil {
- return err
- }
- sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(value))
-
- default:
- return errors.New("contract argument type is not exist")
+ if a.Arguments == nil {
+ return b.AddInput(txInput, sigInst)
+ }
+
+ sigInst = &txbuilder.SigningInstruction{}
+ for _, arg := range a.Arguments {
+ switch arg.Type {
+ case "raw_tx_signature":
+ rawTxSig := &rawTxSigArgument{}
+ if err = json.Unmarshal(arg.RawData, rawTxSig); err != nil {
+ return err
+ }
+
+ // convert path form chainjson.HexBytes to byte
+ var path [][]byte
+ for _, p := range rawTxSig.Path {
+ path = append(path, []byte(p))
+ }
+ sigInst.AddRawWitnessKeys([]chainkd.XPub{rawTxSig.RootXPub}, path, 1)
+
+ case "data":
+ data := &dataArgument{}
+ if err = json.Unmarshal(arg.RawData, data); err != nil {
+ return err
}
+
+ value, err := hex.DecodeString(data.Value)
+ if err != nil {
+ return err
+ }
+ sigInst.WitnessComponents = append(sigInst.WitnessComponents, txbuilder.DataWitness(value))
+
+ default:
+ return errors.New("contract argument type is not exist")
}
}
-
return b.AddInput(txInput, sigInst)
}
"github.com/bytom/errors"
"github.com/bytom/protocol"
"github.com/bytom/protocol/bc"
- "github.com/bytom/sync/idempotency"
)
var (
// UTXO describes an individual account utxo.
type UTXO struct {
- OutputID bc.Hash
- SourceID bc.Hash
-
- // Avoiding AssetAmount here so that new(utxo) doesn't produce an
- // AssetAmount with a nil AssetId.
- AssetID bc.AssetID
- Amount uint64
-
- SourcePos uint64
- ControlProgram []byte
-
+ OutputID bc.Hash
+ SourceID bc.Hash
+ AssetID bc.AssetID
+ Amount uint64
+ SourcePos uint64
+ ControlProgram []byte
AccountID string
Address string
ControlProgramIndex uint64
// reservation describes a reservation of a set of UTXOs belonging
// to a particular account. Reservations are immutable.
type reservation struct {
- ID uint64
- Source source
- UTXOs []*UTXO
- Change uint64
- Expiry time.Time
- ClientToken *string
+ ID uint64
+ Source source
+ UTXOs []*UTXO
+ Change uint64
+ Expiry time.Time
}
func newReserver(c *protocol.Chain, walletdb dbm.DB) *reserver {
//
// To reduce latency and prevent deadlock, no two mutexes (either on
// reserver or sourceReserver) should be held at the same time
-//
-// reserver ensures idempotency of reservations until the reservation
-// expiration.
type reserver struct {
// `sync/atomic` expects the first word in an allocated struct to be 64-bit
// aligned on both ARM and x86-32. See https://goo.gl/zW7dgq for more details.
nextReservationID uint64
c *protocol.Chain
db dbm.DB
- idempotency idempotency.Group
reservationsMu sync.Mutex
reservations map[uint64]*reservation
// Reserve selects and reserves UTXOs according to the criteria provided
// in source. The resulting reservation expires at exp.
-func (re *reserver) Reserve(src source, amount uint64, clientToken *string, exp time.Time) (*reservation, error) {
-
- if clientToken == nil {
- return re.reserve(src, amount, clientToken, exp)
- }
-
- untypedRes, err := re.idempotency.Once(*clientToken, func() (interface{}, error) {
- return re.reserve(src, amount, clientToken, exp)
- })
- return untypedRes.(*reservation), err
-}
-
-func (re *reserver) reserve(src source, amount uint64, clientToken *string, exp time.Time) (res *reservation, err error) {
+func (re *reserver) Reserve(src source, amount uint64, exp time.Time) (res *reservation, err error) {
sourceReserver := re.source(src)
// Try to reserve the right amount.
}
res = &reservation{
- ID: rid,
- Source: src,
- UTXOs: reserved,
- Expiry: exp,
- ClientToken: clientToken,
+ ID: rid,
+ Source: src,
+ UTXOs: reserved,
+ Expiry: exp,
}
// Save the successful reservation.
// ReserveUTXO reserves a specific utxo for spending. The resulting
// reservation expires at exp.
-func (re *reserver) ReserveUTXO(ctx context.Context, out bc.Hash, clientToken *string, exp time.Time) (*reservation, error) {
- if clientToken == nil {
- return re.reserveUTXO(ctx, out, exp, nil)
- }
-
- untypedRes, err := re.idempotency.Once(*clientToken, func() (interface{}, error) {
- return re.reserveUTXO(ctx, out, exp, clientToken)
- })
- return untypedRes.(*reservation), err
-}
-
-func (re *reserver) reserveUTXO(ctx context.Context, out bc.Hash, exp time.Time, clientToken *string) (*reservation, error) {
+func (re *reserver) ReserveUTXO(ctx context.Context, out bc.Hash, exp time.Time) (*reservation, error) {
u, err := findSpecificUTXO(re.db, out)
if err != nil {
return nil, err
}
res := &reservation{
- ID: rid,
- Source: u.source(),
- UTXOs: []*UTXO{u},
- Expiry: exp,
- ClientToken: clientToken,
+ ID: rid,
+ Source: u.source(),
+ UTXOs: []*UTXO{u},
+ Expiry: exp,
}
re.reservationsMu.Lock()
re.reservations[rid] = res
}
re.reservationsMu.Unlock()
- // If we removed any expired reservations, update the corresponding
- // source reservers.
for _, res := range canceled {
re.source(res.Source).cancel(res)
- /*if res.ClientToken != nil {
- re.idempotency.Forget(*res.ClientToken)
- }*/
}
-
- // TODO(jackson): Cleanup any source reservers that don't have
- // anything reserved. It'll be a little tricky because of our
- // locking scheme.
return nil
}
outid := utxo.OutputID
ctx := context.Background()
- res, err := utxoDB.ReserveUTXO(ctx, outid, nil, time.Now())
+ res, err := utxoDB.ReserveUTXO(ctx, outid, time.Now())
if err != nil {
t.Fatal(err)
}
// Verify that the UTXO is reserved.
- _, err = utxoDB.ReserveUTXO(ctx, outid, nil, time.Now())
+ _, err = utxoDB.ReserveUTXO(ctx, outid, time.Now())
if err != ErrReserved {
t.Fatalf("got=%s want=%s", err, ErrReserved)
}
}
// Reserving again should succeed.
- _, err = utxoDB.ReserveUTXO(ctx, outid, nil, time.Now())
+ _, err = utxoDB.ReserveUTXO(ctx, outid, time.Now())
if err != nil {
t.Fatal(err)
}