비트코인 확장 개념 증명: OP_CAT 지원을 통해 비트코인에 브리지 계약 구현

이 기사는 기계로 번역되었습니다
원문 표시

원문: OP_CAT 지원 비트코인에 브리지 규약 구현: 개념 증명

번역 및 교정: 스타크넷 중국 커뮤니티

📑재인쇄시 출처를 꼭 밝혀주세요🕹️

주요 요약 정보

  • 스타크넷의 프로덕션급 브릿지 기반을 마련하기 위해 비트코인 ​​데모 브릿지 계약 구축에 대한 심도 있는 논의

  • 4가지 스마트 계약 구현: 입출금 통합자, 브리지 및 출금 확장자

  • 재귀 계약과 머클 트리를 활용하여 사용자 계정의 무결성과 보안을 유지하면서 입출금 요청을 효율적으로 일괄 처리합니다.

소개

이 기사에서는 sCrypt가 비트코인에 데모 브릿지 계약을 구축하는 방법을 심층적으로 살펴봅니다. 이 개념 증명 구현은 Starknet L2(계층 2) 네트워크를 위한 프로덕션 등급 브리지의 기반을 마련하도록 설계되었습니다. 브릿지 설계를 통해 여러 입금 또는 출금 요청 트랜잭션을 단일 루트 트랜잭션으로 병합하고 메인 브릿지 계약에 통합하여 Merkle 트리로 구성된 일련의 계정으로 구성된 상태를 업데이트할 수 있습니다.

브리지 계약 스크립트는 매우 복잡하기 때문에 sCrypt에서는 sCrypt 도메인별 언어(DSL)를 사용하여 구현을 작성합니다.

개요

브릿지는 재귀적인 계약 비트코인 ​​스크립트로 구성됩니다. 여기서 "계약"은 잠금 스크립트가 지출 거래에 조건을 부과할 수 있다는 것을 의미하고, "재귀"는 위의 규칙이 온체인 지속적인 논리와 상태를 달성할 만큼 충분히 강력하다는 것을 의미합니다(이는 모든 계약 의 기본 요구 사항입니다). -온체인 스마트 계약).

스크립트는 일련의 트랜잭션에 존재하며, 각 트랜잭션은 후속 트랜잭션의 구조에 제약을 가하고 후속 트랜잭션은 현재 트랜잭션의 출력을 잠금 해제합니다. 이 체인에 새로운 트랜잭션이 추가될 때마다 이는 브리지 상태에 대한 업데이트를 나타냅니다. 따라서 이 체인의 끝은 현재 브리지 상태를 유지합니다.

계약 상태, 특히 해시 값은 비소모성 OP_RETURN 출력에 저장됩니다. 이 UTXO를 사용하지는 않지만 계약 스크립트를 실행할 때 해당 데이터를 검사할 수 있습니다. 특히 상태는 아래와 같이 계정 데이터가 포함된 Merkle 트리의 루트 해시를 보유합니다.

이 머클 트리는 고정된 계정 슬롯 세트에 대한 데이터를 보유합니다. 리프 노드에는 주소 및 잔액 포함하여 해당 계정 데이터의 해시가 포함되어 있습니다. 빈 계정 슬롯을 나타내기 위해 이러한 슬롯은 0바이트로 태그 됩니다.

브리지를 업데이트할 때마다 계정 트리가 변경됩니다. 이 업데이트를 용이하게 하기 위해 우리는 비트코인 ​​스크립트에서 검증이 매우 효율적인 Merkle 증명을 사용합니다. 업데이트는 크게 두 단계로 구성됩니다. 먼저, Merkle 트리에 특정 계정의 현재 상태가 포함되어 있음을 보여주기 위해 Merkle 증명을 확인합니다. 그런 다음 계정의 새 상태를 계산한 후 앞서 언급한 Merkle 증명의 동일한 보조 노드를 사용하여 새 루트 해시를 파생합니다.

업데이트는 입금 또는 출금이 될 수 있습니다. 브리지는 단일 트랜잭션에서 이러한 업데이트의 일괄 작업을 수행할 수 있습니다.

보증금

우리의 목표는 사용자가 독립적으로 입금 또는 출금 요청을 제출할 수 있도록 하는 것입니다. 이를 위해 사용자는 각각 입출금 집계 계약에 지불하는 거래를 생성합니다. 계약은 이러한 요청을 Merkle 트리로 집계합니다. 이 트리의 루트 해시는 메인 브리지 계약에 병합될 수 있으며, 이후 모든 입금 또는 출금을 처리합니다.

예금 거래에서 예금 데이터를 해싱하고 머클 트리를 구축하는 것 외에도 계약은 계약 출력에 잠긴 예금 사토시가 트리의 루트 노드에 올바른 방식으로 축적되도록 보장합니다. 집계 계약은 올바른 온체인 스마트 계약만이 이러한 자금을 사용할 수 있도록 보장합니다. (물론 프로덕션 환경에서는 사용자가 입금 거래를 취소할 수도 있습니다.)

이 트리 구조의 설계는 너무 많은 입력과 출력을 포함하는 트랜잭션을 허용하지 않는 계약 스크립트 구성의 제약에서 비롯됩니다. 트리 구조를 통해 잠재적으로 임의의 처리량으로 확장할 수 있습니다.

출금요청

출금 요청 집계는 예금과 유사하지만 몇 가지 차이점이 있습니다. 먼저, 사용자가 자신의 계좌에서 돈을 인출할 수 있도록 인증 방법이 필요합니다. 이는 비트코인 ​​주소를 사용하는 방식과 유사하게 누구나 어떤 계좌에든 돈을 입금할 수 있는 예금과는 다릅니다. 인증은 집계 트리의 리프 노드 수준에서 수행됩니다. 출금 요청 집계 계약은 출금 주소가 리프 트랜잭션에 입력된 첫 번째 P2WPKH 주소와 일치하는지 확인합니다.

이는 주소 소유자가 출금을 요청하는 거래에 서명했기 때문에 출금을 승인한다는 것을 보장합니다. 예금 집계와 비교한 또 다른 미묘한 차이점은 중간 누적 금액도 해시하여 트리 구조로 전달한다는 것입니다. 인출 규모를 조정할 때 이 데이터가 필요하기 때문입니다. 이에 대해서는 나중에 자세히 설명하겠습니다.

기민한 독자는 출금 요청에 대한 이 인증 모델에서 잠재적인 문제를 발견할 수 있습니다. 운영자가 인증되지 않은 가짜 출금 요청을 통해 데이터가 로컬로 위조된 집계 트리의 루트에서 부정 행위를 결정하고 거래를 생성하면 어떻게 될까요? 루트 트랜잭션이 유효한 리프 트랜잭션에서 비롯되었는지 확인하는 효율적인 방법이 필요합니다.

이 문제를 해결하기 위해 소위 "제네시스 체크"를 수행합니다. 본질적으로 우리는 이전 거래와 두 개의 이전 거래인 "조상 거래"를 확인하는 종합 계약을 가지고 있습니다. 계약은 이러한 거래에 동일한 계약 스크립트가 포함되어 있는지 확인하고 동일한 검사를 수행합니다. 이러한 방식으로 귀납적 거래 내역 확인을 구현합니다. 처음 두 트랜잭션은 현재 계약과 동일한 검사를 수행했기 때문에 이러한 트랜잭션의 "조상"도 리프 노드(즉, 제네시스 트랜잭션)까지 동일한 검사를 수행했음을 확인할 수 있습니다.

물론 우리는 트리의 두 가지 모두에 대해 검증을 수행했습니다. 따라서 집계 노드 트랜잭션당 최대 6개의 트랜잭션을 확인합니다.

출금 연장

이제 솔루션의 마지막 부분인 출금 연장에 대해 알아보겠습니다. 일괄 출금 요청을 처리한 후 메인 브릿지 계약은 출력이 총 출금 금액을 확장 계약에 지불하도록 강제합니다. 이 계약은 출금요청 집계 계약이 하는 일의 역과정이라고 생각하면 됩니다. 이는 인출 트리의 루트 노드에서 시작하여 이를 두 개의 가지로 확장합니다. 각 가지에는 해당 가지에 지불할 수 있는 해당 인출 금액이 포함됩니다. 이 프로세스는 철회 트리의 리프 노드까지 계속됩니다. 리프 거래는 클레임 요청한 금액에 대해 계정 소유자의 주소로 간단한 지불 출력을 시행합니다.

구현 방법

브리지 계약을 구현하기 위해 우리는 각각 시스템의 서로 다른 측면을 처리하는 4개의 sCrypt 스마트 계약을 개발했습니다. 이 섹션에서는 각 계약의 기능을 간략하게 설명합니다.

예금 집합자 계약

Deposit Aggregator 계약은 개별 예금을 Merkle 트리로 집계한 다음 메인 브릿지 계약에 병합합니다. 이 집계를 통해 일괄 입금 처리가 가능해지며 브리지에서 개별적으로 처리해야 하는 거래 수가 줄어듭니다. 또한 이를 통해 사용자는 나중에 운영자가 처리할 예금을 독립적으로 제출할 수 있습니다.

 class DepositAggregator extends SmartContract { @prop() operator: PubKey @prop() bridgeSPK: ByteString /** * Covenant used for the aggregation of deposits. * * @param operator - Public key of bridge operator. * @param bridgeSPK - P2TR script of the bridge state covenant. Includes length prefix! */ constructor(operator: PubKey, bridgeSPK: ByteString) { super(...arguments) this.operator = operator this.bridgeSPK = bridgeSPK } @method() public aggregate( shPreimage: SHPreimage, isPrevTxLeaf:boolean, sigOperator: Sig, prevTx0: AggregatorTransaction, prevTx1: AggregatorTransaction, // Additional parameters... ) { // Validation steps... } @method() public finalize( shPreimage: SHPreimage, sigOperator: Sig, prevTx: AggregatorTransaction, ancestorTx0: AggregatorTransaction, ancestorTx1: AggregatorTransaction, bridgeTxId: Sha256, fundingPrevout: ByteString ) { // Finalization steps... } }

계약 구성 함수에는 두 가지 매개변수가 있습니다.

  • Operator: 예치금 집계 권한을 가진 브릿지 운영자의 공개 키입니다.

  • bridgeSPK: 메인 브리지 컨트랙트의 스크립트 공개 키(SPK)로, 총 입금이 올바르게 병합되도록 보장합니다.

예금 집계기의 핵심 기능은 "집계" 방식으로 캡슐화됩니다. 이 방법은 다음 단계를 수행합니다.

Sighash 사전 이미지 및 운영자 서명 확인 : 트랜잭션이 브릿지 운영자에 의해 승인되었는지, Sighash 사전 이미지가 올바른 형식인지, 실행 중인 트랜잭션에 속하는지 확인하십시오. 이 기사 에서 Sighash 사전 이미지 확인에 대해 자세히 알아보세요.

 // Check sighash preimage. const s = SigHashUtils.checkSHPreimage(shPreimage) assert(this.checkSig(s, SigHashUtils.Gx)) // Check operator signature. assert(this.checkSig(sigOperator, this.operator))

이전 트랜잭션 ID 구축 및 유효성 검사 : 집계된 이전 트랜잭션이 유효하고 올바르게 참조되는지 확인합니다.

 // Construct previous transaction ID. const prevTxId = AggregatorUtils.getTxId(prevTx, false) // Verify that the transaction unlocks the specified outputs. const hashPrevouts = AggregatorUtils.getHashPrevouts( bridgeTxId, prevTxId, fundingPrevout ) assert(hashPrevouts == shPreimage.hashPrevouts)

Merkle tree aggregation : 증인 해시로 전달된 입금 데이터가 이전 트랜잭션에 저장된 상태와 일치하는지 확인합니다.

 const hashData0 = DepositAggregator.hashDepositData(depositData0) const hashData1 = DepositAggregator.hashDepositData(depositData1) assert(hashData0 == prevTx0.hashData) assert(hashData1 == prevTx1.hashData)

금액 검증 : 전면 출력의 금액이 지정된 입금액과 일치하는지 확인하여 자금이 집계에서 올바르게 계산되는지 확인합니다.

 // Check that the prev outputs actually carry the specified amount // of satoshis. The amount values can also carry aggregated amounts, // in case we're not aggregating leaves anymore. assert( GeneralUtils.padAmt(depositData0.amount) == prevTx0.outputContractAmt ) assert( GeneralUtils.padAmt(depositData1.amount) == prevTx1.outputContractAmt )

상태 업데이트 : 이전 트랜잭션의 해시 값을 연결하여 새로운 해시 값을 계산하고 OP_RETURN 출력에서 ​​상태를 업데이트합니다.

 // Concatinate hashes from previous aggregation txns (or leaves) // and compute new hash. Store this new hash in the state OP_RETURN // output. const newHash = hash256(prevTx0.hashData + prevTx1.hashData) const stateOut = GeneralUtils.getStateOutput(newHash)

재진입 공격 방지 : 엄격한 출력 스크립트 및 금액을 적용하여 무단 수정이나 이중 지출을 방지합니다.

 // Sum up aggregated amounts and construct contract output. const contractOut = GeneralUtils.getContractOutput( depositData0.amount + depositData1.amount, prevTx0.outputContractSPK ) // Recurse. Send to aggregator with updated hash. const outputs = contractOut + stateOut assert( sha256(outputs) == shPreimage.hashOutputs )

예치금이 집계되면 이를 메인 브리지 계약에 병합해야 합니다. 이 프로세스는 다음 단계를 포함하는 "finalize" 메소드에 의해 처리됩니다.

  • 이전 트랜잭션 확인 : "집계" 방법과 유사하게 이전 트랜잭션을 확인하여 결합된 데이터의 무결성을 확인합니다.

  • 브릿지 컨트랙트 통합 : 브릿지의 거래 ID와 스크립트 공개키를 참조하여 집계된 입금액이 메인 브릿지 컨트랙트에 올바르게 병합되었는지 확인합니다.

예금 집계 계약의 전체 소스 코드는 GitHub에서 볼 수 있습니다.

출금통합자 계약

Withdrawal Aggregator 계약은 예금 집계자가 예금을 처리하는 방식과 유사하게 개별 인출 요청을 Merkle 트리로 집계하도록 설계되었습니다. 그러나 인출 작업에는 합법적인 계정 소유자만 자신의 계정에서 자금을 클레임 할 수 있도록 추가 인증이 필요합니다.

 class WithdrawalAggregator extends SmartContract { @prop() operator: PubKey @prop() bridgeSPK: ByteString /** * Covenant used for the aggregation of withdrawal requests. * * @param operator - Public key of bridge operator. * @param bridgeSPK - P2TR script of the bridge state covenant. Includes length prefix! */ constructor(operator: PubKey, bridgeSPK: ByteString) { super(...arguments) this.operator = operator this.bridgeSPK = bridgeSPK } @method() public aggregate( shPreimage: SHPreimage, isPrevTxLeaf:boolean, sigOperator: Sig, prevTx0: AggregatorTransaction, prevTx1: AggregatorTransaction, // Additional parameters... ) { // Validation and aggregation logic... } @method() public finalize( shPreimage: SHPreimage, sigOperator: Sig, prevTx: AggregatorTransaction, ancestorTx0: AggregatorTransaction, ancestorTx1: AggregatorTransaction, bridgeTxId: Sha256, fundingPrevout: ByteString ) { // Validation logic... } }

출금 집계기의 핵심 기능은 다음 단계를 수행하는 "집계" 방법으로 캡슐화됩니다.

 // Check sighash preimage. const s = SigHashUtils.checkSHPreimage(shPreimage) assert(this.checkSig(s, SigHashUtils.Gx)) // Check operator signature. assert(this.checkSig(sigOperator, this.operator))

선행 트랜잭션 ID 구축 및 유효성 검사 : 이 프로세스에서는 집계된 선행 트랜잭션이 유효하고 올바르게 참조되는지 확인합니다.

 // Construct previous transaction IDs. const prevTxId0 = AggregatorUtils.getTxId(prevTx0, isPrevTxLeaf) const prevTxId1 = AggregatorUtils.getTxId(prevTx1, isPrevTxLeaf) // Verify that the previous transactions are unlocked by the current transaction. const hashPrevouts = AggregatorUtils.getHashPrevouts( prevTxId0, prevTxId1, fundingPrevout ) assert(hashPrevouts == shPreimage.hashPrevouts)

소유권 증명 확인 : 소유권 거래 증명을 확인하면 합법적인 소유자만이 계좌에서 자금을 클레임 할 수 있습니다.

  • 소유권 증명 거래 : 출금 주소의 통제권을 증명하는 거래입니다. 계약은 출금 요청의 주소가 소유권 증명 거래의 주소와 일치하는지 확인합니다.
 if (isPrevTxLeaf) { // Construct ownership proof transaction IDs. const ownershipProofTxId0 = WithdrawalAggregator.getOwnershipProofTxId(ownProofTx0) const ownershipProofTxId1 = WithdrawalAggregator.getOwnershipProofTxId(ownProofTx1) // Check that the leaf transactions unlock the ownership proof transactions. assert(ownershipProofTxId0 + toByteString('0000000000ffffffff') == prevTx0.inputContract0) assert(ownershipProofTxId1 + toByteString('0000000000ffffffff') == prevTx1.inputContract0) // Verify that the withdrawal addresses match the addresses in the ownership proof transactions. assert(withdrawalData0.address == ownProofTx0.outputAddrP2WPKH) assert(withdrawalData1.address == ownProofTx1.outputAddrP2WPKH) }

"조상 거래"를 통한 제네시스 확인 : 예금 집계자와 유사하게 계약은 조상 거래를 검증하여 귀납적 확인을 수행합니다. 이는 거래 내역의 무결성을 보장하고 운영자가 무단 출금 요청을 삽입하는 것을 방지합니다.

 if (!isPrevTxLeaf) { // Construct ancestor transaction IDs. const ancestorTxId0 = AggregatorUtils.getTxId(ancestorTx0, isAncestorLeaf) const ancestorTxId1 = AggregatorUtils.getTxId(ancestorTx1, isAncestorLeaf) const ancestorTxId2 = AggregatorUtils.getTxId(ancestorTx2, isAncestorLeaf) const ancestorTxId3 = AggregatorUtils.getTxId(ancestorTx3, isAncestorLeaf) // Verify that previous transactions unlock the ancestor transactions. assert(prevTx0.inputContract0 == ancestorTxId0 + toByteString('0000000000ffffffff')) assert(prevTx0.inputContract1 == ancestorTxId1 + toByteString('0000000000ffffffff')) assert(prevTx1.inputContract0 == ancestorTxId2 + toByteString('0000000000ffffffff')) assert(prevTx1.inputContract1 == ancestorTxId3 + toByteString('0000000000ffffffff')) // Ensure that the ancestor transactions have the same contract SPK. assert(prevTx0.outputContractSPK == ancestorTx0.outputContractSPK) assert(prevTx0.outputContractSPK == ancestorTx1.outputContractSPK) assert(prevTx0.outputContractSPK == ancestorTx2.outputContractSPK) assert(prevTx0.outputContractSPK == ancestorTx3.outputContractSPK) }

금액 확인 및 총액 계산 : 출금 요청 금액이나 이전 집계 금액을 더해 클레임 할 총액을 계산하는 방식입니다.

 let sumAmt = 0n if (isPrevTxLeaf) { sumAmt = withdrawalData0.amount + withdrawalData1.amount } else { sumAmt = aggregationData0.sumAmt + aggregationData1.sumAmt }

상태 업데이트 : 이전 거래의 해시와 출금 금액의 합을 포함하는 새로운 해시를 계산합니다. 이 해시 값은 상태를 업데이트하기 위해 OP_RETURN 출력에 저장됩니다.

 // Create new aggregation data. const newAggregationData: AggregationData = { prevH0: prevTx0.hashData, prevH1: prevTx1.hashData, sumAmt } const newHash = WithdrawalAggregator.hashAggregationData(newAggregationData) const stateOut = GeneralUtils.getStateOutput(newHash)

재진입 공격 방지 및 출력 적용 : 무단 수정 또는 재진입 공격을 방지하기 위해 출력을 엄격하게 정의합니다.

 // Construct contract output with the minimum dust amount. const contractOut = GeneralUtils.getContractOutput( 546n, prevTx0.outputContractSPK ) // Ensure outputs match the expected format. const outputs = contractOut + stateOut assert( sha256(outputs) == shPreimage.hashOutputs, )

출금 집계 계약의 전체 소스 코드는 GitHub에서 볼 수 있습니다.

교량 계약

브릿지 계약은 우리 시스템의 핵심 구성 요소이자 머클 트리로 구성된 계정과 잔액 포함하여 브릿지의 상태를 유지하기 위한 주요 계약입니다. 앞서 논의한 통합 계약과 통합하여 입출금 작업을 처리합니다.

 class Bridge extends SmartContract { @prop() operator: PubKey @prop() expanderSPK: ByteString constructor( operator: PubKey, expanderSPK: ByteString ) { super(...arguments) this.operator = operator this.expanderSPK = expanderSPK } @method() public deposit( shPreimage: SHPreimage, sigOperator: Sig, prevTx: BridgeTransaction, // Previous bridge update transaction. aggregatorTx: AggregatorTransaction, // Root aggregator transaction. fundingPrevout: ByteString, deposits: FixedArray<DepositData, typeof MAX_NODES_AGGREGATED>, accounts: FixedArray<AccountData, typeof MAX_NODES_AGGREGATED>, depositProofs: FixedArray<MerkleProof, typeof MAX_NODES_AGGREGATED>, accountProofs: FixedArray<MerkleProof, typeof MAX_NODES_AGGREGATED> ) { // Method implementation... } @method() public withdrawal( shPreimage: SHPreimage, sigOperator: Sig, prevTx: BridgeTransaction, // Previous bridge update transaction. aggregatorTx: AggregatorTransaction, // Root aggregator transaction. fundingPrevout: ByteString, withdrawals: FixedArray<WithdrawalData, typeof MAX_NODES_AGGREGATED>, accounts: FixedArray<AccountData, typeof MAX_NODES_AGGREGATED>, intermediateSumsArr: FixedArray<IntermediateValues, typeof MAX_NODES_AGGREGATED>, withdrawalProofs: FixedArray<MerkleProof, typeof MAX_NODES_AGGREGATED>, accountProofs: FixedArray<MerkleProof, typeof MAX_NODES_AGGREGATED> ) { // Method implementation... } }

계약 구성 함수에는 두 가지 매개변수가 있습니다.

  • *operator:* 브릿지 상태를 업데이트할 권한이 있는 브릿지 운영자의 공개 키입니다.

  • ExpanderSPK *: 출금 과정에서 사용되는 WithdrawalExpander 계약의 스크립트 공개 키(SPK)입니다.

입금 방법은 집계된 입금 거래를 처리하고 이에 따라 계좌 잔액 업데이트하는 역할을 담당합니다.

 @method() public deposit( shPreimage: SHPreimage, sigOperator: Sig, prevTx: BridgeTransaction, // Previous bridge update transaction. aggregatorTx: AggregatorTransaction, // Root aggregator transaction. fundingPrevout: ByteString, deposits: FixedArray<DepositData, typeof MAX_NODES_AGGREGATED>, accounts: FixedArray<AccountData, typeof MAX_NODES_AGGREGATED>, depositProofs: FixedArray<MerkleProof, typeof MAX_NODES_AGGREGATED>, accountProofs: FixedArray<MerkleProof, typeof MAX_NODES_AGGREGATED> ) { // Common validation steps... // (Same as in previous contracts: sighash preimage check, operator signature verification, prevouts verification) // Ensure this method is called from the first input. assert(shPreimage.inputNumber == toByteString('00000000')) // Verify that the second input unlocks the correct aggregator script. assert(prevTx.depositAggregatorSPK == aggregatorTx.outputContractSPK) // Process deposits and update accounts. let accountsRootNew: Sha256 = prevTx.accountsRoot let totalAmtDeposited = 0n for (let i = 0; i < MAX_NODES_AGGREGATED; i++) { const deposit = deposits[i] if (deposit.address != toByteString('')) { accountsRootNew = this.applyDeposit( deposits[i], depositProofs[i], aggregatorTx.hashData, accounts[i], accountProofs[i], accountsRootNew ) } totalAmtDeposited += deposit.amount } // Update the bridge state and outputs. // (Compute new state hash, construct contract output, enforce outputs) }

입금 방법으로 수행되는 단계는 다음과 같습니다.

입금 처리 및 계좌 업데이트 :

  • 예금을 반복하고 applyDeposit 메소드를 사용하여 각 예금을 해당 계좌에 적용합니다.

브리지 상태 및 출력 업데이트 :

  • 입금이 처리된 후 새 계정 Merkle 루트가 계산됩니다.

  • 업데이트된 브리지 상태를 나타내는 새 상태 해시를 만듭니다.

  • 총 입금액을 브릿지 잔액 에 추가하는 계약 출력을 구성합니다.

  • 데이터 무결성을 유지하기 위해 출력이 예상된 형식인지 확인합니다.

출금방식은 출금총액 처리, 계좌 잔액 업데이트, 출금확장기를 통해 할당자금을 준비하는 방식입니다.

 @method() public withdrawal( shPreimage: SHPreimage, sigOperator: Sig, prevTx: BridgeTransaction, // Previous bridge update transaction. aggregatorTx: AggregatorTransaction, // Root aggregator transaction. fundingPrevout: ByteString, withdrawals: FixedArray<WithdrawalData, typeof MAX_NODES_AGGREGATED>, accounts: FixedArray<AccountData, typeof MAX_NODES_AGGREGATED>, intermediateSumsArr: FixedArray<IntermediateValues, typeof MAX_NODES_AGGREGATED>, withdrawalProofs: FixedArray<MerkleProof, typeof MAX_NODES_AGGREGATED>, accountProofs: FixedArray<MerkleProof, typeof MAX_NODES_AGGREGATED> ) { // Common validation steps... // (Same as in previous contracts: sighash preimage check, operator signature verification, prevouts verification) // Ensure this method is called from the first input. assert(shPreimage.inputNumber == toByteString('00000000')) // Verify that the second input unlocks the correct aggregator script. assert(prevTx.withdrawalAggregatorSPK == aggregatorTx.outputContractSPK) // Process withdrawals and update accounts. let accountsRootNew: Sha256 = prevTx.accountsRoot let totalAmtWithdrawn = 0n for (let i = 0; i < MAX_NODES_AGGREGATED; i++) { const withdrawal = withdrawals[i] if (withdrawal.address != toByteString('')) { accountsRootNew = this.applyWithdrawal( withdrawal, withdrawalProofs[i], intermediateSumsArr[i], aggregatorTx.hashData, accounts[i], accountProofs[i], accountsRootNew ) } totalAmtWithdrawn += withdrawal.amount } // Update the bridge state and outputs. // (Compute new state hash, construct contract output, create expander output, enforce outputs) }

인출 방법에 따라 수행되는 단계는 다음과 같습니다.

출금 요청 처리 및 계정 업데이트 :

  • 출금 요청을 반복하고 "applyDeposit" 메소드를 사용하여 각 출금을 해당 계좌에 적용합니다.

브리지 상태 및 출력 업데이트 :

  • 출금 처리 후 새 계정 Merkle 루트를 계산합니다.

  • 업데이트된 브리지 상태를 나타내는 새 상태 해시를 만듭니다.

  • 브릿지 잔액 에서 총 출금 금액을 차감하는 계약 출력을 구성합니다.

  • 총 출금 금액이 포함된 출금 연장 계약에 대한 연장 출력을 생성합니다.

  • 데이터 무결성을 유지하기 위해 출력이 예상된 형식인지 확인합니다.

전체 소스 코드는 GitHub에서 볼 수 있습니다.

출금확대 계약

WithdrawalExpander는 브릿지 시스템의 최종 구성 요소이며 출금 요청에 따라 집계된 출금 금액을 개별 사용자에게 다시 분배하는 역할을 합니다. 이는 인출 집계자가 수행한 집계 프로세스를 거꾸로 진행하여 집계된 인출 데이터를 다시 개별 사용자 지불로 확장합니다.

 class WithdrawalExpander extends SmartContract { @prop() operator: PubKey constructor( operator: PubKey ) { super(...arguments) this.operator = operator } @method() public expand( shPreimage: SHPreimage, sigOperator: Sig, // Additional parameters... ) { // Expansion logic... } }

출금 확장기의 핵심 기능은 "확장" 메소드에 캡슐화되어 있습니다. 이 방법은 집계된 출금 데이터를 받아 이를 개별 출금 거래로 반복적으로 확장하여 해당 금액을 사용자에게 지불하는 방식입니다.

리프 노드로 확장 : 방법이 리프 노드(단일 출금)로 확장되면 출금 데이터를 검증하고 사용자의 주소로 직접 지불하는 출력을 구축합니다.

 if (isExpandingLeaves) { // If expanding to leaves, verify the withdrawal data. if (isExpandingPrevTxFirstOutput) { const hashWithdrawalData = WithdrawalAggregator.hashWithdrawalData(withdrawalData0) assert(hashWithdrawalData == prevAggregationData.prevH0) hashOutputs = sha256( WithdrawalExpander.getP2WPKHOut( GeneralUtils.padAmt(withdrawalData0.amount), withdrawalData0.address ) ) } else { const hashWithdrawalData = WithdrawalAggregator.hashWithdrawalData(withdrawalData1) assert(hashWithdrawalData == prevAggregationData.prevH1) hashOutputs = sha256( WithdrawalExpander.getP2WPKHOut( GeneralUtils.padAmt(withdrawalData1.amount), withdrawalData1.address ) ) } }

추가 확장 : 방법이 리프 노드 수준에 도달하지 않은 경우 계속 확장되어 집계 데이터를 두 개의 분기로 분할하고 추가 확장된 트랜잭션을 통해 소비할 출력을 생성합니다.

 else { // Verify current aggregation data matches previous aggregation data. const hashCurrentAggregationData = WithdrawalAggregator.hashAggregationData(currentAggregationData) if (isPrevTxBridge) { assert(hashCurrentAggregationData == prevTxBridge.expanderRoot) } else if (isExpandingPrevTxFirstOutput) { assert(hashCurrentAggregationData == prevAggregationData.prevH0) } else { assert(hashCurrentAggregationData == prevAggregationData.prevH1) } // Prepare outputs for the next level of expansion. let outAmt0 = 0n let outAmt1 = 0n if (isLastAggregationLevel) { const hashWithdrawalData0 = WithdrawalAggregator.hashWithdrawalData(withdrawalData0) const hashWithdrawalData1 = WithdrawalAggregator.hashWithdrawalData(withdrawalData1) assert(hashWithdrawalData0 == currentAggregationData.prevH0) assert(hashWithdrawalData1 == currentAggregationData.prevH1) outAmt0 = withdrawalData0.amount outAmt1 = withdrawalData1.amount } else { const hashNextAggregationData0 = WithdrawalAggregator.hashAggregationData(nextAggregationData0) const hashNextAggregationData1 = WithdrawalAggregator.hashAggregationData(nextAggregationData1) assert(hashNextAggregationData0 == currentAggregationData.prevH0) assert(hashNextAggregationData1 == currentAggregationData.prevH1) outAmt0 = nextAggregationData0.sumAmt outAmt1 = nextAggregationData1.sumAmt } // Construct outputs for further expansion. let expanderSPK = prevTxExpander.contractSPK if (isPrevTxBridge) { expanderSPK = prevTxBridge.expanderSPK } hashOutputs = sha256( GeneralUtils.getContractOutput(outAmt0, expanderSPK) + GeneralUtils.getContractOutput(outAmt1, expanderSPK) + GeneralUtils.getStateOutput(hashCurrentAggregationData) ) }

결론적으로

이 개념 증명 구현에서 우리는 sCrypt 내장 도메인 특정 언어(DSL)를 사용하여 OP_CAT로 구동되는 비트코인 ​​기반 브리지 계약을 개발했습니다. 브릿지는 재귀 계약과 머클 트리를 활용하여 사용자 계정의 무결성과 보안을 유지하면서 입출금 요청을 효율적으로 일괄 처리합니다. Deposit Aggregator , Withdrawal Aggregator , Bridge 및 WithdrawalExpander 의 네 가지 스마트 계약을 설계하고 구현함으로써 우리는 Bitcoin에서 상태 기반 상호 작용을 관리하는 방법을 제공하고 Starknet과 같은 레이어 2 네트워크와의 상호 운용성을 촉진합니다. 이 작업은 생산 등급 브리지 구축을 위한 기술적 기반을 제공하여 잠재적으로 비트코인 ​​생태계의 확장성과 기능성을 향상시킵니다.

모든 코드 구현과 엔드투엔드 테스트는 GitHub에서 확인할 수 있습니다 .

Mirror
면책조항: 상기 내용은 작자의 개인적인 의견입니다. 따라서 이는 Followin의 입장과 무관하며 Followin과 관련된 어떠한 투자 제안도 구성하지 않습니다.
라이크
즐겨찾기에 추가
코멘트