diff --git a/client/v2/algod/algod.go b/client/v2/algod/algod.go index 80681c63..f5ad2205 100644 --- a/client/v2/algod/algod.go +++ b/client/v2/algod/algod.go @@ -80,8 +80,8 @@ func (c *Client) Block(round uint64) *Block { return &Block{c: c, round: round} } -func (c *Client) GetProof(round uint64, txid string) *GetProof { - return &GetProof{c: c, round: round, txid: txid} +func (c *Client) GetTransactionProof(round uint64, txid string) *GetTransactionProof { + return &GetTransactionProof{c: c, round: round, txid: txid} } func (c *Client) Supply() *Supply { @@ -112,6 +112,14 @@ func (c *Client) PendingTransactionInformation(txid string) *PendingTransactionI return &PendingTransactionInformation{c: c, txid: txid} } +func (c *Client) GetStateProof(round uint64) *GetStateProof { + return &GetStateProof{c: c, round: round} +} + +func (c *Client) GetLightBlockHeaderProof(round uint64) *GetLightBlockHeaderProof { + return &GetLightBlockHeaderProof{c: c, round: round} +} + func (c *Client) GetApplicationByID(applicationId uint64) *GetApplicationByID { return &GetApplicationByID{c: c, applicationId: applicationId} } diff --git a/client/v2/algod/getLightBlockHeaderProof.go b/client/v2/algod/getLightBlockHeaderProof.go new file mode 100644 index 00000000..66888891 --- /dev/null +++ b/client/v2/algod/getLightBlockHeaderProof.go @@ -0,0 +1,23 @@ +package algod + +import ( + "context" + "fmt" + + "github.com/algorand/go-algorand-sdk/client/v2/common" + "github.com/algorand/go-algorand-sdk/client/v2/common/models" +) + +// GetLightBlockHeaderProof gets a proof for a given light block header inside a +// state proof commitment +type GetLightBlockHeaderProof struct { + c *Client + + round uint64 +} + +// Do performs the HTTP request +func (s *GetLightBlockHeaderProof) Do(ctx context.Context, headers ...*common.Header) (response models.LightBlockHeaderProof, err error) { + err = s.c.get(ctx, &response, fmt.Sprintf("/v2/blocks/%s/lightheader/proof", common.EscapeParams(s.round)...), nil, headers) + return +} diff --git a/client/v2/algod/getStateProof.go b/client/v2/algod/getStateProof.go new file mode 100644 index 00000000..be65cd11 --- /dev/null +++ b/client/v2/algod/getStateProof.go @@ -0,0 +1,22 @@ +package algod + +import ( + "context" + "fmt" + + "github.com/algorand/go-algorand-sdk/client/v2/common" + "github.com/algorand/go-algorand-sdk/client/v2/common/models" +) + +// GetStateProof get a state proof that covers a given round +type GetStateProof struct { + c *Client + + round uint64 +} + +// Do performs the HTTP request +func (s *GetStateProof) Do(ctx context.Context, headers ...*common.Header) (response models.StateProof, err error) { + err = s.c.get(ctx, &response, fmt.Sprintf("/v2/stateproofs/%s", common.EscapeParams(s.round)...), nil, headers) + return +} diff --git a/client/v2/algod/getProof.go b/client/v2/algod/getTransactionProof.go similarity index 63% rename from client/v2/algod/getProof.go rename to client/v2/algod/getTransactionProof.go index d7e19167..59271184 100644 --- a/client/v2/algod/getProof.go +++ b/client/v2/algod/getTransactionProof.go @@ -8,8 +8,8 @@ import ( "github.com/algorand/go-algorand-sdk/client/v2/common/models" ) -// GetProofParams contains all of the query parameters for url serialization. -type GetProofParams struct { +// GetTransactionProofParams contains all of the query parameters for url serialization. +type GetTransactionProofParams struct { // Format configures whether the response object is JSON or MessagePack encoded. Format string `url:"format,omitempty"` @@ -20,26 +20,26 @@ type GetProofParams struct { Hashtype string `url:"hashtype,omitempty"` } -// GetProof get a Merkle proof for a transaction in a block. -type GetProof struct { +// GetTransactionProof get a proof for a transaction in a block. +type GetTransactionProof struct { c *Client round uint64 txid string - p GetProofParams + p GetTransactionProofParams } // Hashtype the type of hash function used to create the proof, must be one of: // * sha512_256 // * sha256 -func (s *GetProof) Hashtype(Hashtype string) *GetProof { +func (s *GetTransactionProof) Hashtype(Hashtype string) *GetTransactionProof { s.p.Hashtype = Hashtype return s } // Do performs the HTTP request -func (s *GetProof) Do(ctx context.Context, headers ...*common.Header) (response models.ProofResponse, err error) { +func (s *GetTransactionProof) Do(ctx context.Context, headers ...*common.Header) (response models.TransactionProofResponse, err error) { err = s.c.get(ctx, &response, fmt.Sprintf("/v2/blocks/%s/transactions/%s/proof", common.EscapeParams(s.round, s.txid)...), s.p, headers) return } diff --git a/client/v2/common/models/block.go b/client/v2/common/models/block.go index 266d2005..0c433121 100644 --- a/client/v2/common/models/block.go +++ b/client/v2/common/models/block.go @@ -22,6 +22,9 @@ type Block struct { // Seed (seed) Sortition seed. Seed []byte `json:"seed"` + // StateProofTracking tracks the status of state proofs. + StateProofTracking []StateProofTracking `json:"state-proof-tracking,omitempty"` + // Timestamp (ts) Block creation timestamp in seconds since eposh Timestamp uint64 `json:"timestamp"` @@ -37,6 +40,12 @@ type Block struct { // the same TxnRoot. TransactionsRoot []byte `json:"transactions-root"` + // TransactionsRootSha256 (txn256) TransactionsRootSHA256 is an auxiliary + // TransactionRoot, built using a vector commitment instead of a merkle tree, and + // SHA256 hash function instead of the default SHA512_256. This commitment can be + // used on environments where only the SHA256 function exists. + TransactionsRootSha256 []byte `json:"transactions-root-sha256"` + // TxnCounter (tc) TxnCounter counts the number of transactions committed in the // ledger, from the time at which support for this feature was introduced. // Specifically, TxnCounter is the number of the next transaction that will be diff --git a/client/v2/common/models/hash_factory.go b/client/v2/common/models/hash_factory.go new file mode 100644 index 00000000..1de325cf --- /dev/null +++ b/client/v2/common/models/hash_factory.go @@ -0,0 +1,7 @@ +package models + +// HashFactory defines a model for HashFactory. +type HashFactory struct { + // HashType (t) + HashType uint64 `json:"hash-type,omitempty"` +} diff --git a/client/v2/common/models/indexer_state_proof_message.go b/client/v2/common/models/indexer_state_proof_message.go new file mode 100644 index 00000000..316a7c9f --- /dev/null +++ b/client/v2/common/models/indexer_state_proof_message.go @@ -0,0 +1,19 @@ +package models + +// IndexerStateProofMessage defines a model for IndexerStateProofMessage. +type IndexerStateProofMessage struct { + // BlockHeadersCommitment (b) + BlockHeadersCommitment []byte `json:"block-headers-commitment,omitempty"` + + // FirstAttestedRound (f) + FirstAttestedRound uint64 `json:"first-attested-round,omitempty"` + + // LatestAttestedRound (l) + LatestAttestedRound uint64 `json:"latest-attested-round,omitempty"` + + // LnProvenWeight (P) + LnProvenWeight uint64 `json:"ln-proven-weight,omitempty"` + + // VotersCommitment (v) + VotersCommitment []byte `json:"voters-commitment,omitempty"` +} diff --git a/client/v2/common/models/light_block_header_proof.go b/client/v2/common/models/light_block_header_proof.go new file mode 100644 index 00000000..bb5530f7 --- /dev/null +++ b/client/v2/common/models/light_block_header_proof.go @@ -0,0 +1,14 @@ +package models + +// LightBlockHeaderProof proof of membership and position of a light block header. +type LightBlockHeaderProof struct { + // Index the index of the light block header in the vector commitment tree + Index uint64 `json:"index"` + + // Proof the encoded proof. + Proof []byte `json:"proof"` + + // Treedepth represents the depth of the tree that is being proven, i.e. the number + // of edges from a leaf to the root. + Treedepth uint64 `json:"treedepth"` +} diff --git a/client/v2/common/models/merkle_array_proof.go b/client/v2/common/models/merkle_array_proof.go new file mode 100644 index 00000000..3ae363c4 --- /dev/null +++ b/client/v2/common/models/merkle_array_proof.go @@ -0,0 +1,13 @@ +package models + +// MerkleArrayProof defines a model for MerkleArrayProof. +type MerkleArrayProof struct { + // HashFactory + HashFactory HashFactory `json:"hash-factory,omitempty"` + + // Path (pth) + Path [][]byte `json:"path,omitempty"` + + // TreeDepth (td) + TreeDepth uint64 `json:"tree-depth,omitempty"` +} diff --git a/client/v2/common/models/state_proof.go b/client/v2/common/models/state_proof.go new file mode 100644 index 00000000..74459112 --- /dev/null +++ b/client/v2/common/models/state_proof.go @@ -0,0 +1,10 @@ +package models + +// StateProof represents a state proof and its corresponding message +type StateProof struct { + // Message represents the message that the state proofs are attesting to. + Message StateProofMessage `json:"Message"` + + // Stateproof the encoded StateProof for the message. + Stateproof []byte `json:"StateProof"` +} diff --git a/client/v2/common/models/state_proof_fields.go b/client/v2/common/models/state_proof_fields.go new file mode 100644 index 00000000..7d0870f7 --- /dev/null +++ b/client/v2/common/models/state_proof_fields.go @@ -0,0 +1,28 @@ +package models + +// StateProofFields (sp) represents a state proof. +// Definition: +// crypto/stateproof/structs.go : StateProof +type StateProofFields struct { + // PartProofs (P) + PartProofs MerkleArrayProof `json:"part-proofs,omitempty"` + + // PositionsToReveal (pr) Sequence of reveal positions. + PositionsToReveal []uint64 `json:"positions-to-reveal,omitempty"` + + // Reveals (r) Note that this is actually stored as a map[uint64] - Reveal in the + // actual msgp + Reveals []StateProofReveal `json:"reveals,omitempty"` + + // SaltVersion (v) Salt version of the merkle signature. + SaltVersion uint64 `json:"salt-version,omitempty"` + + // SigCommit (c) + SigCommit []byte `json:"sig-commit,omitempty"` + + // SigProofs (S) + SigProofs MerkleArrayProof `json:"sig-proofs,omitempty"` + + // SignedWeight (w) + SignedWeight uint64 `json:"signed-weight,omitempty"` +} diff --git a/client/v2/common/models/state_proof_message.go b/client/v2/common/models/state_proof_message.go new file mode 100644 index 00000000..99f76729 --- /dev/null +++ b/client/v2/common/models/state_proof_message.go @@ -0,0 +1,23 @@ +package models + +// StateProofMessage represents the message that the state proofs are attesting to. +type StateProofMessage struct { + // Blockheaderscommitment the vector commitment root on all light block headers + // within a state proof interval. + Blockheaderscommitment []byte `json:"BlockHeadersCommitment"` + + // Firstattestedround the first round the message attests to. + Firstattestedround uint64 `json:"FirstAttestedRound"` + + // Lastattestedround the last round the message attests to. + Lastattestedround uint64 `json:"LastAttestedRound"` + + // Lnprovenweight an integer value representing the natural log of the proven + // weight with 16 bits of precision. This value would be used to verify the next + // state proof. + Lnprovenweight uint64 `json:"LnProvenWeight"` + + // Voterscommitment the vector commitment root of the top N accounts to sign the + // next StateProof. + Voterscommitment []byte `json:"VotersCommitment"` +} diff --git a/client/v2/common/models/state_proof_participant.go b/client/v2/common/models/state_proof_participant.go new file mode 100644 index 00000000..42a7516d --- /dev/null +++ b/client/v2/common/models/state_proof_participant.go @@ -0,0 +1,10 @@ +package models + +// StateProofParticipant defines a model for StateProofParticipant. +type StateProofParticipant struct { + // Verifier (p) + Verifier StateProofVerifier `json:"verifier,omitempty"` + + // Weight (w) + Weight uint64 `json:"weight,omitempty"` +} diff --git a/client/v2/common/models/state_proof_reveal.go b/client/v2/common/models/state_proof_reveal.go new file mode 100644 index 00000000..68eb702d --- /dev/null +++ b/client/v2/common/models/state_proof_reveal.go @@ -0,0 +1,14 @@ +package models + +// StateProofReveal defines a model for StateProofReveal. +type StateProofReveal struct { + // Participant (p) + Participant StateProofParticipant `json:"participant,omitempty"` + + // Position the position in the signature and participants arrays corresponding to + // this entry. + Position uint64 `json:"position,omitempty"` + + // SigSlot (s) + SigSlot StateProofSigSlot `json:"sig-slot,omitempty"` +} diff --git a/client/v2/common/models/state_proof_sig_slot.go b/client/v2/common/models/state_proof_sig_slot.go new file mode 100644 index 00000000..9d5bd903 --- /dev/null +++ b/client/v2/common/models/state_proof_sig_slot.go @@ -0,0 +1,10 @@ +package models + +// StateProofSigSlot defines a model for StateProofSigSlot. +type StateProofSigSlot struct { + // LowerSigWeight (l) The total weight of signatures in the lower-numbered slots. + LowerSigWeight uint64 `json:"lower-sig-weight,omitempty"` + + // Signature + Signature StateProofSignature `json:"signature,omitempty"` +} diff --git a/client/v2/common/models/state_proof_signature.go b/client/v2/common/models/state_proof_signature.go new file mode 100644 index 00000000..add9909e --- /dev/null +++ b/client/v2/common/models/state_proof_signature.go @@ -0,0 +1,16 @@ +package models + +// StateProofSignature defines a model for StateProofSignature. +type StateProofSignature struct { + // FalconSignature + FalconSignature []byte `json:"falcon-signature,omitempty"` + + // MerkleArrayIndex + MerkleArrayIndex uint64 `json:"merkle-array-index,omitempty"` + + // Proof + Proof MerkleArrayProof `json:"proof,omitempty"` + + // VerifyingKey (vkey) + VerifyingKey []byte `json:"verifying-key,omitempty"` +} diff --git a/client/v2/common/models/state_proof_tracking.go b/client/v2/common/models/state_proof_tracking.go new file mode 100644 index 00000000..8f59f159 --- /dev/null +++ b/client/v2/common/models/state_proof_tracking.go @@ -0,0 +1,18 @@ +package models + +// StateProofTracking defines a model for StateProofTracking. +type StateProofTracking struct { + // NextRound (n) Next round for which we will accept a state proof transaction. + NextRound uint64 `json:"next-round,omitempty"` + + // OnlineTotalWeight (t) The total number of microalgos held by the online accounts + // during the StateProof round. + OnlineTotalWeight uint64 `json:"online-total-weight,omitempty"` + + // Type state Proof Type. Note the raw object uses map with this as key. + Type uint64 `json:"type,omitempty"` + + // VotersCommitment (v) Root of a vector commitment containing online accounts that + // will help sign the proof. + VotersCommitment []byte `json:"voters-commitment,omitempty"` +} diff --git a/client/v2/common/models/state_proof_verifier.go b/client/v2/common/models/state_proof_verifier.go new file mode 100644 index 00000000..2e8bf462 --- /dev/null +++ b/client/v2/common/models/state_proof_verifier.go @@ -0,0 +1,10 @@ +package models + +// StateProofVerifier defines a model for StateProofVerifier. +type StateProofVerifier struct { + // Commitment (cmt) Represents the root of the vector commitment tree. + Commitment []byte `json:"commitment,omitempty"` + + // KeyLifetime (lf) Key lifetime. + KeyLifetime uint64 `json:"key-lifetime,omitempty"` +} diff --git a/client/v2/common/models/transaction.go b/client/v2/common/models/transaction.go index 1974340f..99adadb4 100644 --- a/client/v2/common/models/transaction.go +++ b/client/v2/common/models/transaction.go @@ -133,6 +133,11 @@ type Transaction struct { // signatures should be provided. Signature TransactionSignature `json:"signature,omitempty"` + // StateProofTransaction fields for a state proof transaction. + // Definition: + // data/transactions/stateproof.go : StateProofTxnFields + StateProofTransaction TransactionStateProof `json:"state-proof-transaction,omitempty"` + // Type (type) Indicates what type of transaction this is. Different types have // different fields. // Valid types, and where their fields are stored: @@ -142,5 +147,6 @@ type Transaction struct { // * (axfer) asset-transfer-transaction // * (afrz) asset-freeze-transaction // * (appl) application-transaction + // * (stpf) state-proof-transaction Type string `json:"tx-type,omitempty"` } diff --git a/client/v2/common/models/proof_response.go b/client/v2/common/models/transaction_proof_response.go similarity index 79% rename from client/v2/common/models/proof_response.go rename to client/v2/common/models/transaction_proof_response.go index 6cb41710..535145ce 100644 --- a/client/v2/common/models/proof_response.go +++ b/client/v2/common/models/transaction_proof_response.go @@ -1,7 +1,7 @@ package models -// ProofResponse proof of transaction in a block. -type ProofResponse struct { +// TransactionProofResponse proof of transaction in a block. +type TransactionProofResponse struct { // Hashtype the type of hash function used to create the proof, must be one of: // * sha512_256 // * sha256 @@ -10,7 +10,7 @@ type ProofResponse struct { // Idx index of the transaction in the block's payset. Idx uint64 `json:"idx"` - // Proof merkle proof of transaction membership. + // Proof proof of transaction membership. Proof []byte `json:"proof"` // Stibhash hash of SignedTxnInBlock for verifying proof. diff --git a/client/v2/common/models/transaction_state_proof.go b/client/v2/common/models/transaction_state_proof.go new file mode 100644 index 00000000..419970e3 --- /dev/null +++ b/client/v2/common/models/transaction_state_proof.go @@ -0,0 +1,18 @@ +package models + +// TransactionStateProof fields for a state proof transaction. +// Definition: +// data/transactions/stateproof.go : StateProofTxnFields +type TransactionStateProof struct { + // Message (spmsg) + Message IndexerStateProofMessage `json:"message,omitempty"` + + // StateProof (sp) represents a state proof. + // Definition: + // crypto/stateproof/structs.go : StateProof + StateProof StateProofFields `json:"state-proof,omitempty"` + + // StateProofType (sptype) Type of the state proof. Integer representing an entry + // defined in protocol/stateproof.go + StateProofType uint64 `json:"state-proof-type,omitempty"` +} diff --git a/test/algodclientv2_test.go b/test/algodclientv2_test.go index b8550f99..05c9a854 100644 --- a/test/algodclientv2_test.go +++ b/test/algodclientv2_test.go @@ -48,6 +48,8 @@ func AlgodClientV2Context(s *godog.Suite) { s.Step(`^we make an Account Information call against account "([^"]*)" with exclude "([^"]*)"$`, weMakeAnAccountInformationCallAgainstAccountWithExclude) s.Step(`^we make an Account Asset Information call against account "([^"]*)" assetID (\d+)$`, weMakeAnAccountAssetInformationCallAgainstAccountAssetID) s.Step(`^we make an Account Application Information call against account "([^"]*)" applicationID (\d+)$`, weMakeAnAccountApplicationInformationCallAgainstAccountApplicationID) + s.Step(`^we make a GetLightBlockHeaderProof call for round (\d+)$`, weMakeAGetLightBlockHeaderProofCallForRound) + s.Step(`^we make a GetStateProof call for round (\d+)$`, weMakeAGetStateProofCallForRound) s.BeforeScenario(func(interface{}) { globalErrForExamination = nil @@ -240,3 +242,21 @@ func weMakeAnAccountApplicationInformationCallAgainstAccountApplicationID(accoun algodClient.AccountApplicationInformation(account, uint64(appID)).Do(context.Background()) return nil } + +func weMakeAGetLightBlockHeaderProofCallForRound(round int) error { + algodClient, err := algod.MakeClient(mockServer.URL, "") + if err != nil { + return err + } + algodClient.GetLightBlockHeaderProof(uint64(round)).Do(context.Background()) + return nil +} + +func weMakeAGetStateProofCallForRound(round int) error { + algodClient, err := algod.MakeClient(mockServer.URL, "") + if err != nil { + return err + } + algodClient.GetStateProof(uint64(round)).Do(context.Background()) + return nil +} diff --git a/test/responses_unit_test.go b/test/responses_unit_test.go index 45ad82eb..bf7582f3 100644 --- a/test/responses_unit_test.go +++ b/test/responses_unit_test.go @@ -151,8 +151,10 @@ func weMakeAnyCallTo(client /* algod/indexer */, endpoint string) (err error) { } case "DryRun": response, err = algodC.TealDryrun(models.DryrunRequest{}).Do(context.Background()) + case "GetTransactionProof": + fallthrough case "Proof": - response, err = algodC.GetProof(10, "asdf").Do(context.Background()) + response, err = algodC.GetTransactionProof(10, "asdf").Do(context.Background()) case "GetGenesis": response, err = algodC.GetGenesis().Do(context.Background()) case "AccountApplicationInformation": @@ -161,6 +163,12 @@ func weMakeAnyCallTo(client /* algod/indexer */, endpoint string) (err error) { case "AccountAssetInformation": response, err = algodC.AccountAssetInformation("abc", 123).Do(context.Background()) + case "GetLightBlockHeaderProof": + response, err = + algodC.GetLightBlockHeaderProof(123).Do(context.Background()) + case "GetStateProof": + response, err = + algodC.GetStateProof(123).Do(context.Background()) case "any": // This is an error case // pickup the error as the response diff --git a/types/basics.go b/types/basics.go index 28a102ee..f5456e04 100644 --- a/types/basics.go +++ b/types/basics.go @@ -25,6 +25,8 @@ const ( AssetFreezeTx TxType = "afrz" // ApplicationCallTx allows creating, deleting, and interacting with an application ApplicationCallTx TxType = "appl" + // StateProofTx records a state proof + StateProofTx TxType = "stpf" ) const masterDerivationKeyLenBytes = 32 diff --git a/types/block.go b/types/block.go index 88322db3..d68baa5f 100644 --- a/types/block.go +++ b/types/block.go @@ -17,11 +17,7 @@ type ( // Sortition seed Seed [32]byte `codec:"seed"` - // TxnRoot authenticates the set of transactions appearing in the block. - // More specifically, it's the root of a merkle tree whose leaves are the block's Txids. - // Note that the TxnRoot does not authenticate the signatures on the transactions, only the transactions themselves. - // Two blocks with the same transactions but with different signatures will have the same TxnRoot. - TxnRoot Digest `codec:"txn"` + TxnCommitments // TimeStamp in seconds since epoch TimeStamp int64 `codec:"ts"` @@ -93,11 +89,28 @@ type ( // started being supported). TxnCounter uint64 `codec:"tc"` + // StateProofTracking tracks the status of the state proofs, potentially + // for multiple types of ASPs (Algorand's State Proofs). + //msgp:sort protocol.StateProofType protocol.SortStateProofType + StateProofTracking map[StateProofType]StateProofTrackingData `codec:"spt,allocbound=NumStateProofTypes"` + // ParticipationUpdates contains the information needed to mark // certain accounts offline because their participation keys expired ParticipationUpdates } + // TxnCommitments represents the commitments computed from the transactions in the block. + // It contains multiple commitments based on different algorithms and hash functions, to support different use cases. + TxnCommitments struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + // Root of transaction merkle tree using SHA512_256 hash function. + // This commitment is computed based on the PaysetCommit type specified in the block's consensus protocol. + NativeSha512_256Commitment Digest `codec:"txn"` + + // Root of transaction vector commitment merkle tree using SHA256 hash function + Sha256Commitment Digest `codec:"txn256"` + } + // ParticipationUpdates represents participation account data that // needs to be checked/acted on by the network ParticipationUpdates struct { @@ -167,6 +180,28 @@ type ( NextProtocolSwitchOn Round `codec:"nextswitch"` } + // StateProofTrackingData tracks the status of state proofs. + StateProofTrackingData struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + // StateProofVotersCommitment is the root of a vector commitment containing the + // online accounts that will help sign a state proof. The + // VC root, and the state proof, happen on blocks that + // are a multiple of ConsensusParams.StateProofRounds. For blocks + // that are not a multiple of ConsensusParams.StateProofRounds, + // this value is zero. + StateProofVotersCommitment GenericDigest `codec:"v"` + + // StateProofOnlineTotalWeight is the total number of microalgos held by the online accounts + // during the StateProof round (or zero, if the merkle root is zero - no commitment for StateProof voters). + // This is intended for computing the threshold of votes to expect from StateProofVotersCommitment. + StateProofOnlineTotalWeight MicroAlgos `codec:"t"` + + // StateProofNextRound is the next round for which we will accept + // a StateProof transaction. + StateProofNextRound Round `codec:"n"` + } + // A Block contains the Payset and metadata corresponding to a given Round. Block struct { BlockHeader diff --git a/types/stateproof.go b/types/stateproof.go new file mode 100644 index 00000000..ef403dad --- /dev/null +++ b/types/stateproof.go @@ -0,0 +1,228 @@ +package types + +import ( + "bytes" + "crypto/sha256" + "crypto/sha512" +) + +// StateProofType identifies a particular configuration of state proofs. +type StateProofType uint64 + +const ( + // StateProofBasic is our initial state proof setup. using falcon keys and subset-sum hash + StateProofBasic StateProofType = 0 + + // NumStateProofTypes is the max number of types of state proofs + // that we support. This is used as an allocation bound for a map + // containing different stateproof types in msgpack encoding. + NumStateProofTypes int = 1 + + // MaxReveals is a bound on allocation and on numReveals to limit log computation + MaxReveals int = 640 + + // MaxEncodedTreeDepth is the maximum tree depth (root only depth 0) for a tree which + // is being encoded (either by msbpack or by the fixed length encoding) + MaxEncodedTreeDepth = 16 + + // MaxNumLeavesOnEncodedTree is the maximum number of leaves allowed for a tree which + // is being encoded (either by msbpack or by the fixed length encoding) + MaxNumLeavesOnEncodedTree = 1 << MaxEncodedTreeDepth +) + +// GenericDigest is a digest that implements CustomSizeDigest, and can be used as hash output. +//msgp:allocbound GenericDigest MaxHashDigestSize +type GenericDigest []byte + +// ToSlice is used inside the Tree itself when interacting with TreeDigest +func (d GenericDigest) ToSlice() []byte { return d } + +// IsEqual compare two digests +func (d GenericDigest) IsEqual(other GenericDigest) bool { + return bytes.Equal(d, other) +} + +// IsEmpty checks wether the generic digest is an empty one or not +func (d GenericDigest) IsEmpty() bool { + return len(d) == 0 +} + +// Sumhash512DigestSize The size in bytes of the sumhash checksum +const Sumhash512DigestSize = 64 + +//size of each hash +const ( + Sha512_256Size = sha512.Size256 + SumhashDigestSize = Sumhash512DigestSize + Sha256Size = sha256.Size +) + +// HashType represents different hash functions +type HashType uint16 + +// types of hashes +const ( + Sha512_256 HashType = iota + Sumhash + Sha256 + MaxHashType +) + +// HashFactory is responsible for generating new hashes accordingly to the type it stores. +//msgp:postunmarshalcheck HashFactory Validate +type HashFactory struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + HashType HashType `codec:"t"` +} + +// Proof is used to convince a verifier about membership of leaves: h0,h1...hn +// at indexes i0,i1...in on a tree. The verifier has a trusted value of the tree +// root hash. +type Proof struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + // Path is bounded by MaxNumLeavesOnEncodedTree since there could be multiple reveals, and + // given the distribution of the elt positions and the depth of the tree, + // the path length can increase up to 2^MaxEncodedTreeDepth / 2 + Path []GenericDigest `codec:"pth,allocbound=MaxNumLeavesOnEncodedTree/2"` + HashFactory HashFactory `codec:"hsh"` + // TreeDepth represents the depth of the tree that is being proven. + // It is the number of edges from the root to a leaf. + TreeDepth uint8 `codec:"td"` +} + +const MerkleSignatureSchemeRootSize = SumhashDigestSize + +// Commitment represents the root of the vector commitment tree built upon the MSS keys. +type Commitment [MerkleSignatureSchemeRootSize]byte + +// Verifier is used to verify a merklesignature.Signature produced by merklesignature.Secrets. +type Verifier struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + Commitment Commitment `codec:"cmt"` + KeyLifetime uint64 `codec:"lf"` +} + +// A Participant corresponds to an account whose AccountData.Status +// is Online, and for which the expected sigRound satisfies +// AccountData.VoteFirstValid <= sigRound <= AccountData.VoteLastValid. +// +// In the Algorand ledger, it is possible for multiple accounts to have +// the same PK. Thus, the PK is not necessarily unique among Participants. +// However, each account will produce a unique Participant struct, to avoid +// potential DoS attacks where one account claims to have the same VoteID PK +// as another account. +type Participant struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + // PK is the identifier used to verify the signature for a specific participant + PK Verifier `codec:"p"` + + // Weight is AccountData.MicroAlgos. + Weight uint64 `codec:"w"` +} + +// MerkleSignature represents a Falcon signature in a compressed-form +//msgp:allocbound MerkleSignature FalconMaxSignatureSize +type MerkleSignature []byte + +// SingleLeafProof is used to convince a verifier about membership of a specific +// leaf h at index i on a tree. The verifier has a trusted value of the tree +// root hash. it corresponds to merkle verification path. +type SingleLeafProof struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + Proof +} + +// FalconPublicKeySize pulled out of falcon.go +const FalconPublicKeySize = 0x701 + +// FalconPublicKey is a wrapper for cfalcon.PublicKeySizey (used for packing) +type FalconPublicKey [FalconPublicKeySize]byte + +// FalconVerifier implements the type Verifier interface for the falcon signature scheme. +type FalconVerifier struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + PublicKey FalconPublicKey `codec:"k"` +} + +// FalconSignatureStruct represents a signature in the merkle signature scheme using falcon signatures as an underlying crypto scheme. +// It consists of an ephemeral public key, a signature, a merkle verification path and an index. +// The merkle signature considered valid only if the Signature is verified under the ephemeral public key and +// the Merkle verification path verifies that the ephemeral public key is located at the given index of the tree +// (for the root given in the long-term public key). +// More details can be found on Algorand's spec +type FalconSignatureStruct struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + Signature MerkleSignature `codec:"sig"` + VectorCommitmentIndex uint64 `codec:"idx"` + Proof SingleLeafProof `codec:"prf"` + VerifyingKey FalconVerifier `codec:"vkey"` +} + +// A sigslotCommit is a single slot in the sigs array that forms the state proof. +type sigslotCommit struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + // Sig is a signature by the participant on the expected message. + Sig FalconSignatureStruct `codec:"s"` + + // L is the total weight of signatures in lower-numbered slots. + // This is initialized once the builder has collected a sufficient + // number of signatures. + L uint64 `codec:"l"` +} + +// Reveal is a single array position revealed as part of a state +// proof. It reveals an element of the signature array and +// the corresponding element of the participants array. +type Reveal struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + SigSlot sigslotCommit `codec:"s"` + Part Participant `codec:"p"` +} + +// StateProof represents a proof on Algorand's state. +type StateProof struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + SigCommit GenericDigest `codec:"c"` + SignedWeight uint64 `codec:"w"` + SigProofs Proof `codec:"S"` + PartProofs Proof `codec:"P"` + MerkleSignatureSaltVersion byte `codec:"v"` + // Reveals is a sparse map from the position being revealed + // to the corresponding elements from the sigs and participants + // arrays. + Reveals map[uint64]Reveal `codec:"r,allocbound=MaxReveals"` + PositionsToReveal []uint64 `codec:"pr,allocbound=MaxReveals"` +} + +// Message represents the message that the state proofs are attesting to. This message can be +// used by lightweight client and gives it the ability to verify proofs on the Algorand's state. +// In addition to that proof, this message also contains fields that +// are needed in order to verify the next state proofs (VotersCommitment and LnProvenWeight). +type Message struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + // BlockHeadersCommitment contains a commitment on all light block headers within a state proof interval. + BlockHeadersCommitment []byte `codec:"b,allocbound=Sha256Size"` + VotersCommitment []byte `codec:"v,allocbound=SumhashDigestSize"` + LnProvenWeight uint64 `codec:"P"` + FirstAttestedRound uint64 `codec:"f"` + LastAttestedRound uint64 `codec:"l"` +} + +// StateProofTxnFields captures the fields used for stateproof transactions. +type StateProofTxnFields struct { + _struct struct{} `codec:",omitempty,omitemptyarray"` + + StateProofType StateProofType `codec:"sptype"` + StateProof StateProof `codec:"sp"` + Message Message `codec:"spmsg"` +} diff --git a/types/transaction.go b/types/transaction.go index ad461da8..68a9dbd9 100644 --- a/types/transaction.go +++ b/types/transaction.go @@ -17,6 +17,7 @@ type Transaction struct { AssetTransferTxnFields AssetFreezeTxnFields ApplicationFields + StateProofTxnFields } // SignedTxn wraps a transaction and a signature. The encoding of this struct