Skip to content

Commit

Permalink
merge: branch '3414-submit-anchor-with-placeholder' into 'main'
Browse files Browse the repository at this point in the history
Submit an anchor with a placeholder [#3414]

Closes #3414

See merge request accumulatenetwork/accumulate!931
  • Loading branch information
firelizzard18 committed Oct 9, 2023
2 parents cb7a4f7 + ad08b71 commit 072255c
Show file tree
Hide file tree
Showing 13 changed files with 247 additions and 29 deletions.
34 changes: 27 additions & 7 deletions internal/api/v3/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import (
"gitlab.com/accumulatenetwork/accumulate/internal/core/block/shared"
"gitlab.com/accumulatenetwork/accumulate/internal/database"
"gitlab.com/accumulatenetwork/accumulate/internal/database/indexing"
"gitlab.com/accumulatenetwork/accumulate/internal/logging"
sortutil "gitlab.com/accumulatenetwork/accumulate/internal/util/sort"
"gitlab.com/accumulatenetwork/accumulate/pkg/api/v3"
"gitlab.com/accumulatenetwork/accumulate/pkg/errors"
"gitlab.com/accumulatenetwork/accumulate/pkg/types/merkle"
"gitlab.com/accumulatenetwork/accumulate/pkg/types/messaging"
"gitlab.com/accumulatenetwork/accumulate/pkg/url"
"gitlab.com/accumulatenetwork/accumulate/protocol"
"golang.org/x/exp/slog"
)

func loadMessage(batch *database.Batch, txid *url.TxID) (*api.MessageRecord[messaging.Message], error) {
Expand Down Expand Up @@ -152,12 +154,16 @@ func loadTransactionSignaturesV1(batch *database.Batch, r *api.MessageRecord[mes
}

messages, err := api.MakeRange(entries.Entries(), 0, 0, func(e database.SigSetEntry) (*api.MessageRecord[messaging.Message], error) {
r := new(api.MessageRecord[messaging.Message])

msg, err := batch.Message2(e.SignatureHash[:]).Main().Get()
if err != nil {
return nil, errors.UnknownError.WithFormat("load signature %x: %w", e.SignatureHash[:], err)
slog.Info("Failed to load signature", "error", err, "account", signer.GetUrl(), "hash", logging.AsHex(e.SignatureHash))
r.Error = errors.UnknownError.WithFormat("load signature %x: %w", e.SignatureHash[:], err)
r.Status = r.Error.Code
return r, nil
}

r := new(api.MessageRecord[messaging.Message])
r.ID = msg.ID()
r.Message = msg
return r, nil
Expand Down Expand Up @@ -211,17 +217,24 @@ func loadTransactionSignaturesV2(batch *database.Batch, r *api.MessageRecord[mes

// Load messages
messages, err := api.MakeRange(history, 0, 0, func(i uint64) (*api.MessageRecord[messaging.Message], error) {
r := new(api.MessageRecord[messaging.Message])

hash, err := batch.Account(s).SignatureChain().Entry(int64(i))
if err != nil {
return nil, errors.UnknownError.WithFormat("load %v signature chain entry %d: %w", s, i, err)
slog.Info("Failed to load signature chain entry", "error", err, "account", s, "index", i)
r.Error = errors.UnknownError.WithFormat("load %v signature chain entry %d: %w", s, i, err)
r.Status = r.Error.Code
return r, nil
}

msg, err := batch.Message2(hash).Main().Get()
if err != nil {
return nil, errors.UnknownError.WithFormat("load signature %x: %w", hash, err)
slog.Info("Failed to load signature", "error", err, "account", s, "index", i, "hash", logging.AsHex(hash))
r.Error = errors.UnknownError.WithFormat("load signature %x: %w", hash, err)
r.Status = r.Error.Code
return r, nil
}

r := new(api.MessageRecord[messaging.Message])
r.ID = msg.ID()
r.Message = msg
r.Historical = !active[*(*[32]byte)(hash)] && msg.Type() != messaging.MessageTypeBlockAnchor
Expand Down Expand Up @@ -270,7 +283,11 @@ func loadBlockEntry(batch *database.Batch, entry *protocol.BlockEntry) (*api.Cha

value, err := chain.Entry(int64(entry.Index))
if err != nil {
return r, errors.UnknownError.WithFormat("load %s chain entry %d: %w", entry.Chain, entry.Index, err)
slog.Info("Failed to load chain entry", "error", err, "account", entry.Account, "chain", entry.Chain, "index", entry.Index)
r.Value = &api.ErrorRecord{
Value: errors.UnknownError.WithFormat("load %s chain entry %d: %w", entry.Chain, entry.Index, err),
}
return r, nil
}
r.Entry = *(*[32]byte)(value)

Expand All @@ -284,7 +301,10 @@ func loadBlockEntry(batch *database.Batch, entry *protocol.BlockEntry) (*api.Cha
case merkle.ChainTypeTransaction:
r.Value, err = loadMessage(batch, protocol.UnknownUrl().WithTxID(r.Entry))
if err != nil {
return r, errors.UnknownError.WithFormat("load %s chain entry %d transaction: %w", entry.Chain, entry.Index, err)
slog.Info("Failed to load message", "error", err, "account", entry.Account, "chain", entry.Chain, "index", entry.Index, "hash", logging.AsHex(r.Entry))
r.Value = &api.ErrorRecord{
Value: errors.UnknownError.WithFormat("load %s chain entry %d transaction: %w", entry.Chain, entry.Index, err),
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion internal/api/v3/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ func (s *Querier) searchForTransactionHash(ctx context.Context, batch *database.

default:
// TODO Replace with principal or signer as appropriate
txid := s.partition.WithTxID(msg.ID().Hash())
txid := s.partition.WithTxID(msg.Hash())
r := new(api.RecordRange[*api.TxIDRecord])
r.Total = 1
r.Records = []*api.TxIDRecord{{Value: txid}}
Expand Down
29 changes: 23 additions & 6 deletions internal/core/execute/v2/block/msg_block_anchor.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,38 @@ func (x BlockAnchor) check(ctx *MessageContext, batch *database.Batch) (*messagi
if !ok {
return nil, nil, nil, nil, errors.BadRequest.WithFormat("invalid anchor: expected %v, got %v", messaging.MessageTypeSequenced, anchor.Anchor.Type())
}
txn, ok := seq.Message.(*messaging.TransactionMessage)
txnMsg, ok := seq.Message.(*messaging.TransactionMessage)
if !ok {
return nil, nil, nil, nil, errors.BadRequest.WithFormat("invalid anchor: expected %v, got %v", messaging.MessageTypeTransaction, seq.Message.Type())
}
if typ := txn.GetTransaction().Body.Type(); !typ.IsAnchor() {
return nil, nil, nil, nil, errors.BadRequest.WithFormat("cannot sign a %v transaction with a %v message", typ, anchor.Type())

// Resolve placeholders
txn := txnMsg.Transaction
signed := seq.Hash()
if txn.Body.Type() == protocol.TransactionTypeRemote && ctx.GetActiveGlobals().ExecutorVersion.V2BaikonurEnabled() {
var err error
txn, err = ctx.getTransaction(batch, txn.ID().Hash())
if err != nil {
return nil, nil, nil, nil, errors.UnknownError.WithFormat("load transaction: %w", err)
}

// Recalculate the hash with the full transaction
seq2 := seq.Copy()
seq2.Message = &messaging.TransactionMessage{Transaction: txn}
signed = seq2.Hash()
}

// Verify the transaction is an anchor
if !txn.Body.Type().IsAnchor() {
return nil, nil, nil, nil, errors.BadRequest.WithFormat("cannot sign a %v transaction with a %v message", txn.Body.Type(), anchor.Type())
}

if seq.Source == nil {
return nil, nil, nil, nil, errors.InternalError.WithFormat("sequence is missing source")
}

// Basic validation
h := seq.Hash()
if !anchor.Signature.Verify(nil, h[:]) {
if !anchor.Signature.Verify(nil, signed[:]) {
return nil, nil, nil, nil, errors.Unauthenticated.WithFormat("invalid signature")
}

Expand All @@ -152,7 +169,7 @@ func (x BlockAnchor) check(ctx *MessageContext, batch *database.Batch) (*messagi
return nil, nil, nil, nil, errors.Unauthorized.WithFormat("key is not an active validator for %s", partition)
}

return anchor, txn.GetTransaction(), seq, signer, nil
return anchor, txn, seq, signer, nil
}

func (x BlockAnchor) txnIsReady(batch *database.Batch, ctx *MessageContext, txn *protocol.Transaction, seq *messaging.SequencedMessage) (bool, error) {
Expand Down
5 changes: 5 additions & 0 deletions internal/core/execute/v2/block/msg_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ func (m *MessageContext) callMessageValidator(batch *database.Batch, msg messagi
func (b *bundle) getTransaction(batch *database.Batch, hash [32]byte) (*protocol.Transaction, error) {
// Look in the bundle
for _, msg := range b.messages {
// Look inside block anchors
if blk, ok := msg.(*messaging.BlockAnchor); ok && b.Executor.globals.Active.ExecutorVersion.V2BaikonurEnabled() {
msg = blk.Anchor
}

txn, ok := messaging.UnwrapAs[messaging.MessageWithTransaction](msg)
if ok &&
txn.GetTransaction().Body.Type() != protocol.TransactionTypeRemote &&
Expand Down
60 changes: 50 additions & 10 deletions internal/core/execute/v2/block/msg_sequenced.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,48 @@ func (x SequencedMessage) check(batch *database.Batch, ctx *MessageContext) (*me

// Sequenced messages must either be synthetic or anchors
if !ctx.isWithin(messaging.MessageTypeSynthetic, internal.MessageTypeMessageIsReady) {
if txn, ok := seq.Message.(*messaging.TransactionMessage); !ok || !txn.Transaction.Body.Type().IsAnchor() {
isAnchor, err := x.isAnchor(batch, ctx, seq)
if err != nil {
return nil, errors.UnknownError.Wrap(err)
}
if !isAnchor {
return nil, errors.BadRequest.WithFormat("invalid payload for sequenced message")
}
}

// Load the transaction
if txn, ok := seq.Message.(*messaging.TransactionMessage); ok {
_, err := x.resolveTransaction(batch, txn)
if err != nil {
return nil, errors.UnknownError.Wrap(err)
if !ctx.GetActiveGlobals().ExecutorVersion.V2BaikonurEnabled() {
if txn, ok := seq.Message.(*messaging.TransactionMessage); ok {
_, err := x.resolveTransaction(batch, txn)
if err != nil {
return nil, errors.UnknownError.Wrap(err)
}
}
}

return seq, nil
}

func (x SequencedMessage) isAnchor(batch *database.Batch, ctx *MessageContext, seq *messaging.SequencedMessage) (bool, error) {
msg, ok := seq.Message.(*messaging.TransactionMessage)
switch {
case ok && msg.Transaction.Body.Type().IsAnchor():
return true, nil

case !ok,
!ctx.GetActiveGlobals().ExecutorVersion.V2BaikonurEnabled(),
msg.Transaction.Body.Type() != protocol.TransactionTypeRemote:
return false, nil

}

txn, err := ctx.getTransaction(batch, msg.Hash())
if err != nil {
return false, errors.UnknownError.Wrap(err)
}
return txn.Body.Type().IsAnchor(), nil
}

func (x SequencedMessage) Process(batch *database.Batch, ctx *MessageContext) (_ *protocol.TransactionStatus, err error) {
batch = batch.Begin(true)
defer func() { commitOrDiscard(batch, &err) }()
Expand Down Expand Up @@ -126,8 +152,19 @@ func (x SequencedMessage) process(batch *database.Batch, ctx *MessageContext, se

var st *protocol.TransactionStatus
if ready {
// Copy to avoid issues with resolving remote transactions. If the
// transaction is a placeholder (a remote transaction), the executor
// will resolve the full transaction and replace the placeholder. If we
// don't copy, that causes the sequenced message to change, which
// changes its hash, which causes problems with recording it in the
// database.
msg := seq.Message
if ctx.GetActiveGlobals().ExecutorVersion.V2BaikonurEnabled() {
msg = msg.CopyAsInterface().(messaging.Message)
}

// Process the message within
st, err = ctx.callMessageExecutor(batch, seq.Message)
st, err = ctx.callMessageExecutor(batch, msg)
} else {
// Mark the message as pending
ctx.Executor.logger.Debug("Pending sequenced message", "hash", logging.AsHex(seq.Message.Hash()).Slice(0, 4), "module", "synthetic")
Expand Down Expand Up @@ -223,16 +260,19 @@ func (x SequencedMessage) updateLedger(batch *database.Batch, ctx *MessageContex
return partLedger, nil
}

func (SequencedMessage) loadLedger(batch *database.Batch, ctx *MessageContext, seq *messaging.SequencedMessage) (bool, protocol.SequenceLedger, error) {
func (x SequencedMessage) loadLedger(batch *database.Batch, ctx *MessageContext, seq *messaging.SequencedMessage) (bool, protocol.SequenceLedger, error) {
var isAnchor bool
u := ctx.Executor.Describe.Synthetic()
if txn, ok := seq.Message.(*messaging.TransactionMessage); ok && txn.Transaction.Body.Type().IsAnchor() {
isAnchor = true
isAnchor, err := x.isAnchor(batch, ctx, seq)
if err != nil {
return false, nil, errors.UnknownError.Wrap(err)
}
if isAnchor {
u = ctx.Executor.Describe.AnchorPool()
}

var ledger protocol.SequenceLedger
err := batch.Account(u).Main().GetAs(&ledger)
err = batch.Account(u).Main().GetAs(&ledger)
if err != nil {
msg := "synthetic"
if isAnchor {
Expand Down
2 changes: 1 addition & 1 deletion internal/core/execute/v2/block/msg_synthetic.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (SyntheticMessage) check(batch *database.Batch, ctx *MessageContext) (*mess
}

// Verify the signature
h := syn.Message.ID().Hash()
h := syn.Message.Hash()
if !syn.Signature.Verify(nil, h[:]) {
return nil, errors.BadRequest.With("invalid signature")
}
Expand Down
11 changes: 10 additions & 1 deletion internal/logging/types.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022 The Accumulate Authors
// Copyright 2023 The Accumulate Authors
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
Expand All @@ -12,6 +12,8 @@ import (
"fmt"
"reflect"
"strings"

"golang.org/x/exp/slog"
)

type LogAsHex interface {
Expand All @@ -20,12 +22,19 @@ type LogAsHex interface {

type LogAsHexValue []byte

var _ json.Marshaler = LogAsHexValue{}
var _ slog.LogValuer = LogAsHexValue{}

func (v LogAsHexValue) MarshalJSON() ([]byte, error) {
b := make([]byte, hex.EncodedLen(len(v)))
hex.Encode(b, v)
return json.Marshal(strings.ToUpper(string(b)))
}

func (v LogAsHexValue) LogValue() slog.Value {
return slog.StringValue(hex.EncodeToString(v))
}

func (v LogAsHexValue) Slice(i, j int) LogAsHex {
if i < 0 {
i = 0
Expand Down
24 changes: 24 additions & 0 deletions internal/node/abci/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"gitlab.com/accumulatenetwork/accumulate/internal/logging"
"gitlab.com/accumulatenetwork/accumulate/pkg/errors"
"gitlab.com/accumulatenetwork/accumulate/pkg/types/messaging"
"gitlab.com/accumulatenetwork/accumulate/pkg/url"
"gitlab.com/accumulatenetwork/accumulate/protocol"
)

Expand Down Expand Up @@ -46,6 +47,8 @@ func executeTransactions(logger log.Logger, execute executeFunc, raw []byte) ([]
}
}

AdjustStatusIDs(deliveries, results)

// If the results can't be marshaled, provide no results but do not fail the
// batch
rset, err := (&protocol.TransactionResultSet{Results: results}).MarshalBinary()
Expand All @@ -56,3 +59,24 @@ func executeTransactions(logger log.Logger, execute executeFunc, raw []byte) ([]

return deliveries, results, rset, nil
}

// AdjustStatusIDs corrects for the fact that the block anchor's ID method was
// bad and has been changed.
func AdjustStatusIDs(messages []messaging.Message, st []*protocol.TransactionStatus) {
adjustedIDs := map[[32]byte]*url.TxID{}
for _, msg := range messages {
switch msg := msg.(type) {
case *messaging.BlockAnchor:
adjustedIDs[msg.Hash()] = msg.OldID()
}
}
for _, st := range st {
if st.TxID == nil {
continue
}
id, ok := adjustedIDs[st.TxID.Hash()]
if ok {
st.TxID = id
}
}
}
4 changes: 4 additions & 0 deletions pkg/types/messaging/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ func (m *SyntheticMessage) ID() *url.TxID {
}

func (m *BlockAnchor) ID() *url.TxID {
return m.Signature.GetSigner().WithTxID(m.Hash())
}

func (m *BlockAnchor) OldID() *url.TxID {
return m.Signature.GetSigner().WithTxID(*(*[32]byte)(m.Signature.Hash()))
}

Expand Down
4 changes: 4 additions & 0 deletions protocol/transaction_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ func (t *Transaction) ID() *url.TxID {
return t.Header.Principal.WithTxID(*(*[32]byte)(t.GetHash()))
}

func (t *Transaction) Hash() [32]byte {
return *(*[32]byte)(t.GetHash())
}

// Hash calculates the hash of the transaction as H(H(header) + H(body)).
func (t *Transaction) GetHash() []byte {
t.calcHash()
Expand Down
Loading

0 comments on commit 072255c

Please sign in to comment.